create-better-fullstack 1.0.0-canary.c9b74dcc → 1.1.0
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 +27 -66
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +15 -3
- package/dist/index.mjs +1 -1
- package/dist/{src-CwAb8MVO.mjs → src-BkBCdTQg.mjs} +623 -48
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,110 +1,71 @@
|
|
|
1
1
|
# Better Fullstack CLI
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A CLI-first toolkit for building Full Stack applications. Skip the configuration. Ship the code.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
Run without installing globally:
|
|
8
|
-
|
|
9
7
|
```bash
|
|
10
8
|
# Using bun (recommended)
|
|
11
|
-
bun create better-
|
|
9
|
+
bun create better-fullstack@latest
|
|
12
10
|
|
|
13
11
|
# Using pnpm
|
|
14
|
-
pnpm create better-
|
|
12
|
+
pnpm create better-fullstack@latest
|
|
15
13
|
|
|
16
14
|
# Using npm
|
|
17
|
-
npx create-better-
|
|
15
|
+
npx create-better-fullstack@latest
|
|
18
16
|
```
|
|
19
17
|
|
|
20
|
-
Follow the prompts to configure your project or use the `--yes` flag for defaults.
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
| Category | Options |
|
|
25
|
-
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
-
| **TypeScript** | End-to-end type safety across all parts of your application |
|
|
27
|
-
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
|
28
|
-
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
|
|
29
|
-
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
|
30
|
-
| **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
|
|
31
|
-
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
|
32
|
-
| **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
|
|
33
|
-
| **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres<br>• MongoDB Atlas<br>• None (manual setup) |
|
|
34
|
-
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
|
|
35
|
-
| **Styling** | Tailwind CSS with shadcn/ui components |
|
|
36
|
-
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Lefthook, Husky (Git hooks)<br>• Turborepo (optimized builds) |
|
|
37
|
-
| **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
|
|
38
|
-
| **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |
|
|
39
|
-
|
|
40
18
|
## Usage
|
|
41
19
|
|
|
42
20
|
```bash
|
|
43
|
-
Usage: create-better-
|
|
21
|
+
Usage: create-better-fullstack [project-directory] [options]
|
|
44
22
|
|
|
45
23
|
Options:
|
|
46
24
|
-V, --version Output the version number
|
|
47
25
|
-y, --yes Use default configuration
|
|
26
|
+
--yolo Random configuration (experimental)
|
|
48
27
|
--database <type> Database type (none, sqlite, postgres, mysql, mongodb)
|
|
49
|
-
--orm <type> ORM type (
|
|
50
|
-
--auth
|
|
51
|
-
--
|
|
52
|
-
--frontend <types...> Frontend types
|
|
53
|
-
--
|
|
28
|
+
--orm <type> ORM type (drizzle, prisma, mongoose, typeorm, kysely, mikroorm, sequelize, none)
|
|
29
|
+
--auth <type> Authentication (better-auth, clerk, nextauth, none)
|
|
30
|
+
--payments <type> Payments (polar, stripe, lemon-squeezy, paddle, dodo, none)
|
|
31
|
+
--frontend <types...> Frontend types
|
|
32
|
+
--backend <framework> Backend framework
|
|
33
|
+
--runtime <runtime> Runtime (bun, node, workers, none)
|
|
34
|
+
--api <type> API type (trpc, orpc, ts-rest, garph, none)
|
|
35
|
+
--addons <types...> Additional addons
|
|
54
36
|
--examples <types...> Examples to include (todo, ai, none)
|
|
55
|
-
--git
|
|
56
|
-
--no-git Skip git initialization
|
|
37
|
+
--git / --no-git Initialize git repository
|
|
57
38
|
--package-manager <pm> Package manager (npm, pnpm, bun)
|
|
58
|
-
--install
|
|
59
|
-
--
|
|
60
|
-
--db-setup <setup> Database setup (turso, d1, neon, supabase, prisma-postgres, mongodb-atlas, docker, none)
|
|
61
|
-
--web-deploy <setup> Web deployment (workers, alchemy, none)
|
|
62
|
-
--server-deploy <setup> Server deployment (workers, alchemy, none)
|
|
63
|
-
--backend <framework> Backend framework (hono, express, elysia, next, convex, fastify, none)
|
|
64
|
-
--runtime <runtime> Runtime (bun, node, workers, none)
|
|
65
|
-
--api <type> API type (trpc, orpc, none)
|
|
39
|
+
--install / --no-install Install dependencies
|
|
40
|
+
--db-setup <setup> Database setup
|
|
66
41
|
-h, --help Display help
|
|
67
42
|
```
|
|
68
43
|
|
|
69
44
|
## Examples
|
|
70
45
|
|
|
71
|
-
Create a project with default configuration:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
npx create-better-t-stack --yes
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Create a project with specific options:
|
|
78
|
-
|
|
79
46
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
47
|
+
# Default configuration
|
|
48
|
+
npx create-better-fullstack --yes
|
|
82
49
|
|
|
83
|
-
|
|
50
|
+
# With specific options
|
|
51
|
+
npx create-better-fullstack --database postgres --orm drizzle --auth better-auth --addons pwa biome
|
|
84
52
|
|
|
85
|
-
|
|
86
|
-
npx create-better-
|
|
87
|
-
```
|
|
53
|
+
# Elysia backend with Node.js
|
|
54
|
+
npx create-better-fullstack --backend elysia --runtime node
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npx create-better-t-stack --frontend tanstack-router native-bare
|
|
56
|
+
# Multiple frontends (web + native)
|
|
57
|
+
npx create-better-fullstack --frontend tanstack-router native-bare
|
|
93
58
|
```
|
|
94
59
|
|
|
95
60
|
## Project Structure
|
|
96
61
|
|
|
97
|
-
The created project follows a clean monorepo structure:
|
|
98
|
-
|
|
99
62
|
```
|
|
100
|
-
my-
|
|
63
|
+
my-app/
|
|
101
64
|
├── apps/
|
|
102
65
|
│ ├── web/ # Frontend application
|
|
103
66
|
│ ├── server/ # Backend API
|
|
104
67
|
│ ├── native/ # (optional) Mobile application
|
|
105
68
|
│ └── docs/ # (optional) Documentation site
|
|
106
69
|
├── packages/ # Shared packages
|
|
107
|
-
└── README.md
|
|
70
|
+
└── README.md
|
|
108
71
|
```
|
|
109
|
-
|
|
110
|
-
After project creation, you'll receive detailed instructions for next steps and additional setup requirements.
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -162,6 +162,16 @@ declare const router: {
|
|
|
162
162
|
none: "none";
|
|
163
163
|
opentelemetry: "opentelemetry";
|
|
164
164
|
}>>;
|
|
165
|
+
cms: z.ZodOptional<z.ZodEnum<{
|
|
166
|
+
none: "none";
|
|
167
|
+
payload: "payload";
|
|
168
|
+
sanity: "sanity";
|
|
169
|
+
strapi: "strapi";
|
|
170
|
+
}>>;
|
|
171
|
+
caching: z.ZodOptional<z.ZodEnum<{
|
|
172
|
+
none: "none";
|
|
173
|
+
"upstash-redis": "upstash-redis";
|
|
174
|
+
}>>;
|
|
165
175
|
frontend: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
166
176
|
none: "none";
|
|
167
177
|
"tanstack-router": "tanstack-router";
|
|
@@ -458,7 +468,7 @@ declare function createBtsCli(): trpc_cli0.TrpcCli;
|
|
|
458
468
|
*
|
|
459
469
|
* @example
|
|
460
470
|
* ```typescript
|
|
461
|
-
* import { create } from "create-better-
|
|
471
|
+
* import { create } from "create-better-fullstack";
|
|
462
472
|
*
|
|
463
473
|
* const result = await create("my-app", {
|
|
464
474
|
* frontend: ["tanstack-router"],
|
|
@@ -484,7 +494,7 @@ declare function builder(): Promise<void>;
|
|
|
484
494
|
*
|
|
485
495
|
* @example
|
|
486
496
|
* ```typescript
|
|
487
|
-
* import { createVirtual, EMBEDDED_TEMPLATES } from "create-better-
|
|
497
|
+
* import { createVirtual, EMBEDDED_TEMPLATES } from "create-better-fullstack";
|
|
488
498
|
*
|
|
489
499
|
* const result = await createVirtual({
|
|
490
500
|
* frontend: ["tanstack-router"],
|
|
@@ -510,7 +520,9 @@ type Animation = import__better_fullstack_types.Animation;
|
|
|
510
520
|
type Auth = import__better_fullstack_types.Auth;
|
|
511
521
|
type Backend = import__better_fullstack_types.Backend;
|
|
512
522
|
type BetterTStackConfig = import__better_fullstack_types.BetterTStackConfig;
|
|
523
|
+
type CMS = import__better_fullstack_types.CMS;
|
|
513
524
|
type CSSFramework = import__better_fullstack_types.CSSFramework;
|
|
525
|
+
type Caching = import__better_fullstack_types.Caching;
|
|
514
526
|
type CreateInput = import__better_fullstack_types.CreateInput;
|
|
515
527
|
type Database = import__better_fullstack_types.Database;
|
|
516
528
|
type DatabaseSetup = import__better_fullstack_types.DatabaseSetup;
|
|
@@ -536,4 +548,4 @@ type ServerDeploy = import__better_fullstack_types.ServerDeploy;
|
|
|
536
548
|
type Template = import__better_fullstack_types.Template;
|
|
537
549
|
type UILibrary = import__better_fullstack_types.UILibrary;
|
|
538
550
|
type WebDeploy = import__better_fullstack_types.WebDeploy;
|
|
539
|
-
export { type API, type Addons, type Animation, type Auth, type Backend, type BetterTStackConfig, type CSSFramework, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
|
551
|
+
export { type API, type Addons, type Animation, type Auth, type Backend, type BetterTStackConfig, type CMS, type CSSFramework, type Caching, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-
|
|
2
|
+
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-BkBCdTQg.mjs";
|
|
3
3
|
|
|
4
4
|
export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
|
@@ -1038,6 +1038,119 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1038
1038
|
return response;
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
+
//#endregion
|
|
1042
|
+
//#region src/prompts/ai.ts
|
|
1043
|
+
async function getAIChoice(ai) {
|
|
1044
|
+
if (ai !== void 0) return ai;
|
|
1045
|
+
const response = await navigableSelect({
|
|
1046
|
+
message: "Select AI SDK",
|
|
1047
|
+
options: [
|
|
1048
|
+
{
|
|
1049
|
+
value: "vercel-ai",
|
|
1050
|
+
label: "Vercel AI SDK",
|
|
1051
|
+
hint: "The AI Toolkit for TypeScript - supports OpenAI, Anthropic, Google, etc."
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
value: "mastra",
|
|
1055
|
+
label: "Mastra",
|
|
1056
|
+
hint: "TypeScript-native AI agent framework with workflows"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
value: "voltagent",
|
|
1060
|
+
label: "VoltAgent",
|
|
1061
|
+
hint: "AI Agent framework with memory, workflows, and observability"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
value: "langgraph",
|
|
1065
|
+
label: "LangGraph.js",
|
|
1066
|
+
hint: "Graph-based agent orchestration with stateful workflows"
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
value: "openai-agents",
|
|
1070
|
+
label: "OpenAI Agents SDK",
|
|
1071
|
+
hint: "Official multi-agent framework with handoffs and guardrails"
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
value: "google-adk",
|
|
1075
|
+
label: "Google ADK",
|
|
1076
|
+
hint: "Code-first agent development kit for building AI agents"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
value: "modelfusion",
|
|
1080
|
+
label: "ModelFusion",
|
|
1081
|
+
hint: "Type-safe AI library for multi-provider text generation"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
value: "langchain",
|
|
1085
|
+
label: "LangChain",
|
|
1086
|
+
hint: "Build context-aware reasoning applications"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
value: "llamaindex",
|
|
1090
|
+
label: "LlamaIndex",
|
|
1091
|
+
hint: "Data framework for LLM applications"
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
value: "none",
|
|
1095
|
+
label: "None",
|
|
1096
|
+
hint: "No AI SDK"
|
|
1097
|
+
}
|
|
1098
|
+
],
|
|
1099
|
+
initialValue: "none"
|
|
1100
|
+
});
|
|
1101
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1102
|
+
return response;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/prompts/animation.ts
|
|
1107
|
+
async function getAnimationChoice(animation, frontends) {
|
|
1108
|
+
if (animation !== void 0) return animation;
|
|
1109
|
+
const { web } = splitFrontends(frontends);
|
|
1110
|
+
if (web.length === 0) return "none";
|
|
1111
|
+
const isReact = web.some((f) => [
|
|
1112
|
+
"tanstack-router",
|
|
1113
|
+
"react-router",
|
|
1114
|
+
"tanstack-start",
|
|
1115
|
+
"next",
|
|
1116
|
+
"redwood"
|
|
1117
|
+
].includes(f));
|
|
1118
|
+
const options = [];
|
|
1119
|
+
if (isReact) options.push({
|
|
1120
|
+
value: "framer-motion",
|
|
1121
|
+
label: "Framer Motion",
|
|
1122
|
+
hint: "Production-ready declarative animations for React"
|
|
1123
|
+
}, {
|
|
1124
|
+
value: "react-spring",
|
|
1125
|
+
label: "React Spring",
|
|
1126
|
+
hint: "Physics-based animations for fluid interactions"
|
|
1127
|
+
});
|
|
1128
|
+
options.push({
|
|
1129
|
+
value: "gsap",
|
|
1130
|
+
label: "GSAP",
|
|
1131
|
+
hint: "Professional-grade animation engine for the web"
|
|
1132
|
+
}, {
|
|
1133
|
+
value: "auto-animate",
|
|
1134
|
+
label: "Auto Animate",
|
|
1135
|
+
hint: "Zero-config, drop-in animation utility"
|
|
1136
|
+
}, {
|
|
1137
|
+
value: "lottie",
|
|
1138
|
+
label: "Lottie",
|
|
1139
|
+
hint: "Render After Effects animations natively"
|
|
1140
|
+
}, {
|
|
1141
|
+
value: "none",
|
|
1142
|
+
label: "None",
|
|
1143
|
+
hint: "Skip animation library setup"
|
|
1144
|
+
});
|
|
1145
|
+
const response = await navigableSelect({
|
|
1146
|
+
message: "Select animation library",
|
|
1147
|
+
options,
|
|
1148
|
+
initialValue: "none"
|
|
1149
|
+
});
|
|
1150
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1151
|
+
return response;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1041
1154
|
//#endregion
|
|
1042
1155
|
//#region src/prompts/api.ts
|
|
1043
1156
|
async function getApiChoice(Api, frontend, backend, astroIntegration) {
|
|
@@ -1226,6 +1339,63 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
1226
1339
|
return response;
|
|
1227
1340
|
}
|
|
1228
1341
|
|
|
1342
|
+
//#endregion
|
|
1343
|
+
//#region src/prompts/caching.ts
|
|
1344
|
+
async function getCachingChoice(caching, backend) {
|
|
1345
|
+
if (caching !== void 0) return caching;
|
|
1346
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1347
|
+
const response = await navigableSelect({
|
|
1348
|
+
message: "Select caching solution",
|
|
1349
|
+
options: [{
|
|
1350
|
+
value: "upstash-redis",
|
|
1351
|
+
label: "Upstash Redis",
|
|
1352
|
+
hint: "Serverless Redis with REST API for edge and serverless"
|
|
1353
|
+
}, {
|
|
1354
|
+
value: "none",
|
|
1355
|
+
label: "None",
|
|
1356
|
+
hint: "Skip caching layer setup"
|
|
1357
|
+
}],
|
|
1358
|
+
initialValue: "none"
|
|
1359
|
+
});
|
|
1360
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1361
|
+
return response;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region src/prompts/cms.ts
|
|
1366
|
+
async function getCMSChoice(cms, backend) {
|
|
1367
|
+
if (cms !== void 0) return cms;
|
|
1368
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1369
|
+
const response = await navigableSelect({
|
|
1370
|
+
message: "Select headless CMS",
|
|
1371
|
+
options: [
|
|
1372
|
+
{
|
|
1373
|
+
value: "payload",
|
|
1374
|
+
label: "Payload",
|
|
1375
|
+
hint: "TypeScript-first headless CMS with Next.js integration"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
value: "sanity",
|
|
1379
|
+
label: "Sanity",
|
|
1380
|
+
hint: "Real-time collaborative CMS with schema-as-code"
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
value: "strapi",
|
|
1384
|
+
label: "Strapi",
|
|
1385
|
+
hint: "Open-source headless CMS with admin panel"
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
value: "none",
|
|
1389
|
+
label: "None",
|
|
1390
|
+
hint: "Skip headless CMS setup"
|
|
1391
|
+
}
|
|
1392
|
+
],
|
|
1393
|
+
initialValue: "none"
|
|
1394
|
+
});
|
|
1395
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1396
|
+
return response;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1229
1399
|
//#endregion
|
|
1230
1400
|
//#region src/prompts/css-framework.ts
|
|
1231
1401
|
const CSS_FRAMEWORK_OPTIONS = {
|
|
@@ -1495,6 +1665,98 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
|
|
|
1495
1665
|
return response;
|
|
1496
1666
|
}
|
|
1497
1667
|
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/prompts/file-upload.ts
|
|
1670
|
+
async function getFileUploadChoice(fileUpload, backend) {
|
|
1671
|
+
if (fileUpload !== void 0) return fileUpload;
|
|
1672
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1673
|
+
const response = await navigableSelect({
|
|
1674
|
+
message: "Select file upload solution",
|
|
1675
|
+
options: [
|
|
1676
|
+
{
|
|
1677
|
+
value: "uploadthing",
|
|
1678
|
+
label: "UploadThing",
|
|
1679
|
+
hint: "TypeScript-first file uploads with built-in validation"
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
value: "filepond",
|
|
1683
|
+
label: "FilePond",
|
|
1684
|
+
hint: "Flexible file upload with image preview and drag & drop"
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
value: "uppy",
|
|
1688
|
+
label: "Uppy",
|
|
1689
|
+
hint: "Modular file uploader with resumable uploads and plugins"
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
value: "none",
|
|
1693
|
+
label: "None",
|
|
1694
|
+
hint: "Skip file upload integration"
|
|
1695
|
+
}
|
|
1696
|
+
],
|
|
1697
|
+
initialValue: "none"
|
|
1698
|
+
});
|
|
1699
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1700
|
+
return response;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
//#endregion
|
|
1704
|
+
//#region src/prompts/forms.ts
|
|
1705
|
+
async function getFormsChoice(forms, frontends) {
|
|
1706
|
+
if (forms !== void 0) return forms;
|
|
1707
|
+
const { web } = splitFrontends(frontends);
|
|
1708
|
+
if (web.length === 0) return "none";
|
|
1709
|
+
const isReact = web.some((f) => [
|
|
1710
|
+
"tanstack-router",
|
|
1711
|
+
"react-router",
|
|
1712
|
+
"tanstack-start",
|
|
1713
|
+
"next",
|
|
1714
|
+
"redwood"
|
|
1715
|
+
].includes(f));
|
|
1716
|
+
const isSolid = web.includes("solid");
|
|
1717
|
+
const isQwik = web.includes("qwik");
|
|
1718
|
+
const options = [];
|
|
1719
|
+
if (isReact) options.push({
|
|
1720
|
+
value: "react-hook-form",
|
|
1721
|
+
label: "React Hook Form",
|
|
1722
|
+
hint: "Performant, flexible form validation library"
|
|
1723
|
+
}, {
|
|
1724
|
+
value: "formik",
|
|
1725
|
+
label: "Formik",
|
|
1726
|
+
hint: "Popular form state management with Yup validation"
|
|
1727
|
+
}, {
|
|
1728
|
+
value: "final-form",
|
|
1729
|
+
label: "Final Form",
|
|
1730
|
+
hint: "Framework-agnostic form state management"
|
|
1731
|
+
}, {
|
|
1732
|
+
value: "conform",
|
|
1733
|
+
label: "Conform",
|
|
1734
|
+
hint: "Progressive enhancement forms with Zod validation"
|
|
1735
|
+
});
|
|
1736
|
+
options.push({
|
|
1737
|
+
value: "tanstack-form",
|
|
1738
|
+
label: "TanStack Form",
|
|
1739
|
+
hint: "Fully-typed, framework-agnostic form library"
|
|
1740
|
+
});
|
|
1741
|
+
if (isSolid || isQwik) options.push({
|
|
1742
|
+
value: "modular-forms",
|
|
1743
|
+
label: "Modular Forms",
|
|
1744
|
+
hint: "Type-safe forms for Solid and Qwik (3KB bundle)"
|
|
1745
|
+
});
|
|
1746
|
+
options.push({
|
|
1747
|
+
value: "none",
|
|
1748
|
+
label: "None",
|
|
1749
|
+
hint: "Build custom form handling"
|
|
1750
|
+
});
|
|
1751
|
+
const response = await navigableSelect({
|
|
1752
|
+
message: "Select form library",
|
|
1753
|
+
options,
|
|
1754
|
+
initialValue: isReact ? "react-hook-form" : "tanstack-form"
|
|
1755
|
+
});
|
|
1756
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1757
|
+
return response;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1498
1760
|
//#endregion
|
|
1499
1761
|
//#region src/prompts/frontend.ts
|
|
1500
1762
|
async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
@@ -1637,6 +1899,76 @@ async function getinstallChoice(install) {
|
|
|
1637
1899
|
return response;
|
|
1638
1900
|
}
|
|
1639
1901
|
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/prompts/job-queue.ts
|
|
1904
|
+
async function getJobQueueChoice(jobQueue, backend) {
|
|
1905
|
+
if (jobQueue !== void 0) return jobQueue;
|
|
1906
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1907
|
+
const response = await navigableSelect({
|
|
1908
|
+
message: "Select job queue solution",
|
|
1909
|
+
options: [
|
|
1910
|
+
{
|
|
1911
|
+
value: "bullmq",
|
|
1912
|
+
label: "BullMQ",
|
|
1913
|
+
hint: "Redis-backed job queue for background tasks and scheduling"
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
value: "trigger-dev",
|
|
1917
|
+
label: "Trigger.dev",
|
|
1918
|
+
hint: "Background jobs as code with serverless execution"
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
value: "inngest",
|
|
1922
|
+
label: "Inngest",
|
|
1923
|
+
hint: "Event-driven functions with built-in queuing and scheduling"
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
value: "temporal",
|
|
1927
|
+
label: "Temporal",
|
|
1928
|
+
hint: "Durable workflow orchestration for reliable distributed systems"
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
value: "none",
|
|
1932
|
+
label: "None",
|
|
1933
|
+
hint: "Skip job queue/background worker setup"
|
|
1934
|
+
}
|
|
1935
|
+
],
|
|
1936
|
+
initialValue: "none"
|
|
1937
|
+
});
|
|
1938
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1939
|
+
return response;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
//#endregion
|
|
1943
|
+
//#region src/prompts/logging.ts
|
|
1944
|
+
async function getLoggingChoice(logging, backend) {
|
|
1945
|
+
if (logging !== void 0) return logging;
|
|
1946
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1947
|
+
const response = await navigableSelect({
|
|
1948
|
+
message: "Select logging framework",
|
|
1949
|
+
options: [
|
|
1950
|
+
{
|
|
1951
|
+
value: "pino",
|
|
1952
|
+
label: "Pino",
|
|
1953
|
+
hint: "Fast JSON logger with minimal overhead"
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
value: "winston",
|
|
1957
|
+
label: "Winston",
|
|
1958
|
+
hint: "Flexible logging library with multiple transports"
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
value: "none",
|
|
1962
|
+
label: "None",
|
|
1963
|
+
hint: "Skip logging framework setup"
|
|
1964
|
+
}
|
|
1965
|
+
],
|
|
1966
|
+
initialValue: "none"
|
|
1967
|
+
});
|
|
1968
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1969
|
+
return response;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1640
1972
|
//#endregion
|
|
1641
1973
|
//#region src/prompts/navigable-group.ts
|
|
1642
1974
|
/**
|
|
@@ -1694,6 +2026,28 @@ async function navigableGroup(prompts, opts) {
|
|
|
1694
2026
|
return results;
|
|
1695
2027
|
}
|
|
1696
2028
|
|
|
2029
|
+
//#endregion
|
|
2030
|
+
//#region src/prompts/observability.ts
|
|
2031
|
+
async function getObservabilityChoice(observability, backend) {
|
|
2032
|
+
if (observability !== void 0) return observability;
|
|
2033
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
2034
|
+
const response = await navigableSelect({
|
|
2035
|
+
message: "Select observability solution",
|
|
2036
|
+
options: [{
|
|
2037
|
+
value: "opentelemetry",
|
|
2038
|
+
label: "OpenTelemetry",
|
|
2039
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
2040
|
+
}, {
|
|
2041
|
+
value: "none",
|
|
2042
|
+
label: "None",
|
|
2043
|
+
hint: "Skip observability/tracing setup"
|
|
2044
|
+
}],
|
|
2045
|
+
initialValue: "none"
|
|
2046
|
+
});
|
|
2047
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2048
|
+
return response;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
1697
2051
|
//#endregion
|
|
1698
2052
|
//#region src/prompts/orm.ts
|
|
1699
2053
|
const ormOptions = {
|
|
@@ -1805,6 +2159,56 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
|
1805
2159
|
return response;
|
|
1806
2160
|
}
|
|
1807
2161
|
|
|
2162
|
+
//#endregion
|
|
2163
|
+
//#region src/prompts/realtime.ts
|
|
2164
|
+
async function getRealtimeChoice(realtime, backend) {
|
|
2165
|
+
if (realtime !== void 0) return realtime;
|
|
2166
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
2167
|
+
const response = await navigableSelect({
|
|
2168
|
+
message: "Select real-time solution",
|
|
2169
|
+
options: [
|
|
2170
|
+
{
|
|
2171
|
+
value: "socket-io",
|
|
2172
|
+
label: "Socket.IO",
|
|
2173
|
+
hint: "Real-time bidirectional communication with fallbacks"
|
|
2174
|
+
},
|
|
2175
|
+
{
|
|
2176
|
+
value: "partykit",
|
|
2177
|
+
label: "PartyKit",
|
|
2178
|
+
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
value: "ably",
|
|
2182
|
+
label: "Ably",
|
|
2183
|
+
hint: "Real-time messaging platform with pub/sub and presence"
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
value: "pusher",
|
|
2187
|
+
label: "Pusher",
|
|
2188
|
+
hint: "Real-time communication APIs with channels and events"
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
value: "liveblocks",
|
|
2192
|
+
label: "Liveblocks",
|
|
2193
|
+
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
value: "yjs",
|
|
2197
|
+
label: "Y.js",
|
|
2198
|
+
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
value: "none",
|
|
2202
|
+
label: "None",
|
|
2203
|
+
hint: "Skip real-time/WebSocket integration"
|
|
2204
|
+
}
|
|
2205
|
+
],
|
|
2206
|
+
initialValue: "none"
|
|
2207
|
+
});
|
|
2208
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2209
|
+
return response;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
1808
2212
|
//#endregion
|
|
1809
2213
|
//#region src/prompts/runtime.ts
|
|
1810
2214
|
async function getRuntimeChoice(runtime, backend) {
|
|
@@ -1843,6 +2247,115 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
1843
2247
|
return "none";
|
|
1844
2248
|
}
|
|
1845
2249
|
|
|
2250
|
+
//#endregion
|
|
2251
|
+
//#region src/prompts/state-management.ts
|
|
2252
|
+
async function getStateManagementChoice(stateManagement, frontends) {
|
|
2253
|
+
if (stateManagement !== void 0) return stateManagement;
|
|
2254
|
+
const { web } = splitFrontends(frontends);
|
|
2255
|
+
if (web.length === 0) return "none";
|
|
2256
|
+
const isReact = web.some((f) => [
|
|
2257
|
+
"tanstack-router",
|
|
2258
|
+
"react-router",
|
|
2259
|
+
"tanstack-start",
|
|
2260
|
+
"next",
|
|
2261
|
+
"redwood"
|
|
2262
|
+
].includes(f));
|
|
2263
|
+
const options = [];
|
|
2264
|
+
if (isReact) options.push({
|
|
2265
|
+
value: "zustand",
|
|
2266
|
+
label: "Zustand",
|
|
2267
|
+
hint: "Lightweight state management with simple API"
|
|
2268
|
+
}, {
|
|
2269
|
+
value: "jotai",
|
|
2270
|
+
label: "Jotai",
|
|
2271
|
+
hint: "Primitive and flexible atomic state"
|
|
2272
|
+
}, {
|
|
2273
|
+
value: "redux-toolkit",
|
|
2274
|
+
label: "Redux Toolkit",
|
|
2275
|
+
hint: "Enterprise-standard state with excellent TS support"
|
|
2276
|
+
}, {
|
|
2277
|
+
value: "valtio",
|
|
2278
|
+
label: "Valtio",
|
|
2279
|
+
hint: "Proxy-based state management"
|
|
2280
|
+
}, {
|
|
2281
|
+
value: "legend-state",
|
|
2282
|
+
label: "Legend State",
|
|
2283
|
+
hint: "High-performance observable state for React"
|
|
2284
|
+
}, {
|
|
2285
|
+
value: "mobx",
|
|
2286
|
+
label: "MobX",
|
|
2287
|
+
hint: "Observable-based reactive state management"
|
|
2288
|
+
});
|
|
2289
|
+
options.push({
|
|
2290
|
+
value: "nanostores",
|
|
2291
|
+
label: "Nanostores",
|
|
2292
|
+
hint: "Tiny state manager (1KB) for all frameworks"
|
|
2293
|
+
}, {
|
|
2294
|
+
value: "xstate",
|
|
2295
|
+
label: "XState",
|
|
2296
|
+
hint: "State machines and statecharts for complex logic"
|
|
2297
|
+
}, {
|
|
2298
|
+
value: "tanstack-store",
|
|
2299
|
+
label: "TanStack Store",
|
|
2300
|
+
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
2301
|
+
}, {
|
|
2302
|
+
value: "none",
|
|
2303
|
+
label: "None",
|
|
2304
|
+
hint: "Skip state management setup"
|
|
2305
|
+
});
|
|
2306
|
+
const response = await navigableSelect({
|
|
2307
|
+
message: "Select state management",
|
|
2308
|
+
options,
|
|
2309
|
+
initialValue: "none"
|
|
2310
|
+
});
|
|
2311
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2312
|
+
return response;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
//#endregion
|
|
2316
|
+
//#region src/prompts/testing.ts
|
|
2317
|
+
async function getTestingChoice(testing) {
|
|
2318
|
+
if (testing !== void 0) return testing;
|
|
2319
|
+
const response = await navigableSelect({
|
|
2320
|
+
message: "Select testing framework",
|
|
2321
|
+
options: [
|
|
2322
|
+
{
|
|
2323
|
+
value: "vitest",
|
|
2324
|
+
label: "Vitest",
|
|
2325
|
+
hint: "Blazing fast Vite-native unit test framework"
|
|
2326
|
+
},
|
|
2327
|
+
{
|
|
2328
|
+
value: "vitest-playwright",
|
|
2329
|
+
label: "Vitest + Playwright",
|
|
2330
|
+
hint: "Both unit and E2E testing for complete coverage"
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
value: "playwright",
|
|
2334
|
+
label: "Playwright",
|
|
2335
|
+
hint: "End-to-end testing framework by Microsoft"
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
value: "jest",
|
|
2339
|
+
label: "Jest",
|
|
2340
|
+
hint: "Classic testing framework with wide ecosystem"
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
value: "cypress",
|
|
2344
|
+
label: "Cypress",
|
|
2345
|
+
hint: "E2E testing with time travel debugging"
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
value: "none",
|
|
2349
|
+
label: "None",
|
|
2350
|
+
hint: "Skip testing framework setup"
|
|
2351
|
+
}
|
|
2352
|
+
],
|
|
2353
|
+
initialValue: "vitest"
|
|
2354
|
+
});
|
|
2355
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2356
|
+
return response;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
1846
2359
|
//#endregion
|
|
1847
2360
|
//#region src/prompts/ui-library.ts
|
|
1848
2361
|
const UI_LIBRARY_OPTIONS = {
|
|
@@ -1915,6 +2428,60 @@ async function getUILibraryChoice(uiLibrary, frontends) {
|
|
|
1915
2428
|
return selected;
|
|
1916
2429
|
}
|
|
1917
2430
|
|
|
2431
|
+
//#endregion
|
|
2432
|
+
//#region src/prompts/validation.ts
|
|
2433
|
+
async function getValidationChoice(validation) {
|
|
2434
|
+
if (validation !== void 0) return validation;
|
|
2435
|
+
const response = await navigableSelect({
|
|
2436
|
+
message: "Select validation library",
|
|
2437
|
+
options: [
|
|
2438
|
+
{
|
|
2439
|
+
value: "zod",
|
|
2440
|
+
label: "Zod",
|
|
2441
|
+
hint: "TypeScript-first schema validation (recommended)"
|
|
2442
|
+
},
|
|
2443
|
+
{
|
|
2444
|
+
value: "valibot",
|
|
2445
|
+
label: "Valibot",
|
|
2446
|
+
hint: "Smaller bundle alternative to Zod (~1KB)"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
value: "arktype",
|
|
2450
|
+
label: "ArkType",
|
|
2451
|
+
hint: "TypeScript-first validation, 2-4x faster than Zod"
|
|
2452
|
+
},
|
|
2453
|
+
{
|
|
2454
|
+
value: "typebox",
|
|
2455
|
+
label: "TypeBox",
|
|
2456
|
+
hint: "JSON Schema type builder for TypeScript"
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
value: "typia",
|
|
2460
|
+
label: "Typia",
|
|
2461
|
+
hint: "Super-fast validation via compile-time transform"
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
value: "runtypes",
|
|
2465
|
+
label: "Runtypes",
|
|
2466
|
+
hint: "Runtime type validation with composable validators"
|
|
2467
|
+
},
|
|
2468
|
+
{
|
|
2469
|
+
value: "effect-schema",
|
|
2470
|
+
label: "@effect/schema",
|
|
2471
|
+
hint: "Effect ecosystem schema validation with powerful transformations"
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
value: "none",
|
|
2475
|
+
label: "None",
|
|
2476
|
+
hint: "Use Zod internally only (no additional library)"
|
|
2477
|
+
}
|
|
2478
|
+
],
|
|
2479
|
+
initialValue: "zod"
|
|
2480
|
+
});
|
|
2481
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2482
|
+
return response;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
1918
2485
|
//#endregion
|
|
1919
2486
|
//#region src/prompts/web-deploy.ts
|
|
1920
2487
|
function hasWebFrontend(frontends) {
|
|
@@ -1980,6 +2547,19 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1980
2547
|
dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
|
|
1981
2548
|
webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
|
|
1982
2549
|
serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
|
|
2550
|
+
ai: () => getAIChoice(flags.ai),
|
|
2551
|
+
validation: () => getValidationChoice(flags.validation),
|
|
2552
|
+
forms: ({ results }) => getFormsChoice(flags.forms, results.frontend),
|
|
2553
|
+
stateManagement: ({ results }) => getStateManagementChoice(flags.stateManagement, results.frontend),
|
|
2554
|
+
animation: ({ results }) => getAnimationChoice(flags.animation, results.frontend),
|
|
2555
|
+
testing: () => getTestingChoice(flags.testing),
|
|
2556
|
+
realtime: ({ results }) => getRealtimeChoice(flags.realtime, results.backend),
|
|
2557
|
+
jobQueue: ({ results }) => getJobQueueChoice(flags.jobQueue, results.backend),
|
|
2558
|
+
fileUpload: ({ results }) => getFileUploadChoice(flags.fileUpload, results.backend),
|
|
2559
|
+
logging: ({ results }) => getLoggingChoice(flags.logging, results.backend),
|
|
2560
|
+
observability: ({ results }) => getObservabilityChoice(flags.observability, results.backend),
|
|
2561
|
+
cms: ({ results }) => getCMSChoice(flags.cms, results.backend),
|
|
2562
|
+
caching: ({ results }) => getCachingChoice(flags.caching, results.backend),
|
|
1983
2563
|
git: () => getGitChoice(flags.git),
|
|
1984
2564
|
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
|
1985
2565
|
install: () => getinstallChoice(flags.install)
|
|
@@ -2009,19 +2589,19 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
2009
2589
|
api: result.api,
|
|
2010
2590
|
webDeploy: result.webDeploy,
|
|
2011
2591
|
serverDeploy: result.serverDeploy,
|
|
2012
|
-
ai:
|
|
2013
|
-
stateManagement:
|
|
2014
|
-
validation:
|
|
2015
|
-
forms:
|
|
2016
|
-
testing:
|
|
2017
|
-
realtime:
|
|
2018
|
-
jobQueue:
|
|
2019
|
-
animation:
|
|
2020
|
-
fileUpload:
|
|
2021
|
-
logging:
|
|
2022
|
-
observability:
|
|
2023
|
-
cms:
|
|
2024
|
-
caching:
|
|
2592
|
+
ai: result.ai,
|
|
2593
|
+
stateManagement: result.stateManagement,
|
|
2594
|
+
validation: result.validation,
|
|
2595
|
+
forms: result.forms,
|
|
2596
|
+
testing: result.testing,
|
|
2597
|
+
realtime: result.realtime,
|
|
2598
|
+
jobQueue: result.jobQueue,
|
|
2599
|
+
animation: result.animation,
|
|
2600
|
+
fileUpload: result.fileUpload,
|
|
2601
|
+
logging: result.logging,
|
|
2602
|
+
observability: result.observability,
|
|
2603
|
+
cms: result.cms,
|
|
2604
|
+
caching: result.caching,
|
|
2025
2605
|
ecosystem: flags.ecosystem ?? "typescript",
|
|
2026
2606
|
rustWebFramework: flags.rustWebFramework ?? "none",
|
|
2027
2607
|
rustFrontend: flags.rustFrontend ?? "none",
|
|
@@ -2099,7 +2679,7 @@ const getLatestCLIVersion = () => {
|
|
|
2099
2679
|
*/
|
|
2100
2680
|
function isTelemetryEnabled() {
|
|
2101
2681
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
2102
|
-
const BTS_TELEMETRY = "
|
|
2682
|
+
const BTS_TELEMETRY = "1";
|
|
2103
2683
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
2104
2684
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
2105
2685
|
return true;
|
|
@@ -2193,11 +2773,11 @@ function generateReproducibleCommand(config) {
|
|
|
2193
2773
|
flags.push(config.git ? "--git" : "--no-git");
|
|
2194
2774
|
flags.push(`--package-manager ${config.packageManager}`);
|
|
2195
2775
|
flags.push(config.install ? "--install" : "--no-install");
|
|
2196
|
-
let baseCommand = "npx create-better-
|
|
2776
|
+
let baseCommand = "npx create-better-fullstack@latest";
|
|
2197
2777
|
const pkgManager = config.packageManager;
|
|
2198
|
-
if (pkgManager === "bun") baseCommand = "bun create better-
|
|
2199
|
-
else if (pkgManager === "pnpm") baseCommand = "pnpm create better-
|
|
2200
|
-
else if (pkgManager === "npm") baseCommand = "npx create-better-
|
|
2778
|
+
if (pkgManager === "bun") baseCommand = "bun create better-fullstack@latest";
|
|
2779
|
+
else if (pkgManager === "pnpm") baseCommand = "pnpm create better-fullstack@latest";
|
|
2780
|
+
else if (pkgManager === "npm") baseCommand = "npx create-better-fullstack@latest";
|
|
2201
2781
|
const projectPathArg = config.relativePath ? ` ${config.relativePath}` : "";
|
|
2202
2782
|
return `${baseCommand}${projectPathArg} ${flags.join(" ")}`;
|
|
2203
2783
|
}
|
|
@@ -2295,31 +2875,24 @@ const TITLE_TEXT = `
|
|
|
2295
2875
|
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
2296
2876
|
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
2297
2877
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2878
|
+
███████╗██╗ ██╗██╗ ██╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
2879
|
+
██╔════╝██║ ██║██║ ██║ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
2880
|
+
█████╗ ██║ ██║██║ ██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
2881
|
+
██╔══╝ ██║ ██║██║ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
2882
|
+
██║ ╚██████╔╝███████╗███████╗███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
2883
|
+
╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
2304
2884
|
`;
|
|
2305
|
-
const
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
peach: "#FAB387",
|
|
2311
|
-
yellow: "#F9E2AF",
|
|
2312
|
-
green: "#A6E3A1",
|
|
2313
|
-
teal: "#94E2D5",
|
|
2314
|
-
sky: "#89DCEB",
|
|
2315
|
-
sapphire: "#74C7EC",
|
|
2316
|
-
lavender: "#B4BEFE"
|
|
2885
|
+
const monochromeTheme = {
|
|
2886
|
+
white: "#FFFFFF",
|
|
2887
|
+
lightGray: "#E5E5E5",
|
|
2888
|
+
gray: "#A3A3A3",
|
|
2889
|
+
darkGray: "#737373"
|
|
2317
2890
|
};
|
|
2318
2891
|
const renderTitle = () => {
|
|
2319
2892
|
const terminalWidth = process.stdout.columns || 80;
|
|
2320
2893
|
const titleLines = TITLE_TEXT.split("\n");
|
|
2321
|
-
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(
|
|
2322
|
-
else console.log(gradient(Object.values(
|
|
2894
|
+
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(monochromeTheme)).multiline(`Better Fullstack`));
|
|
2895
|
+
else console.log(gradient(Object.values(monochromeTheme)).multiline(TITLE_TEXT));
|
|
2323
2896
|
};
|
|
2324
2897
|
|
|
2325
2898
|
//#endregion
|
|
@@ -2468,9 +3041,9 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
2468
3041
|
const has = (k) => flags ? flags.has(k) : true;
|
|
2469
3042
|
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") exitWithError("Mongoose ORM requires MongoDB database. Please use '--database mongodb' or choose a different ORM.");
|
|
2470
3043
|
if (has("orm") && has("database") && orm === "drizzle" && db === "mongodb") exitWithError("Drizzle ORM does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2471
|
-
if (has("orm") && has("database") && orm === "typeorm" && db === "mongodb") exitWithError("TypeORM does not support MongoDB in Better
|
|
3044
|
+
if (has("orm") && has("database") && orm === "typeorm" && db === "mongodb") exitWithError("TypeORM does not support MongoDB in Better Fullstack. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2472
3045
|
if (has("orm") && has("database") && orm === "kysely" && db === "mongodb") exitWithError("Kysely does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2473
|
-
if (has("orm") && has("database") && orm === "mikroorm" && db === "mongodb") exitWithError("MikroORM does not support MongoDB in Better
|
|
3046
|
+
if (has("orm") && has("database") && orm === "mikroorm" && db === "mongodb") exitWithError("MikroORM does not support MongoDB in Better Fullstack. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2474
3047
|
if (has("orm") && has("database") && orm === "sequelize" && db === "mongodb") exitWithError("Sequelize does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2475
3048
|
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") exitWithError("MongoDB database requires Mongoose or Prisma ORM. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
2476
3049
|
if (has("database") && has("orm") && db && db !== "none" && orm === "none") exitWithError("Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', '--orm typeorm', '--orm kysely', '--orm mikroorm', '--orm sequelize', or '--orm mongoose'.");
|
|
@@ -2781,7 +3354,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
2781
3354
|
rustLibraries: projectConfig.rustLibraries
|
|
2782
3355
|
};
|
|
2783
3356
|
const baseContent = {
|
|
2784
|
-
$schema: "https://
|
|
3357
|
+
$schema: "https://better-fullstack-web.vercel.app/schema.json",
|
|
2785
3358
|
version: btsConfig.version,
|
|
2786
3359
|
createdAt: btsConfig.createdAt,
|
|
2787
3360
|
ecosystem: btsConfig.ecosystem,
|
|
@@ -2828,7 +3401,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
2828
3401
|
eol: "\n"
|
|
2829
3402
|
});
|
|
2830
3403
|
configContent = JSONC.applyEdits(configContent, formatResult);
|
|
2831
|
-
const finalContent = `// Better
|
|
3404
|
+
const finalContent = `// Better Fullstack configuration file
|
|
2832
3405
|
// safe to delete
|
|
2833
3406
|
|
|
2834
3407
|
${configContent}`;
|
|
@@ -5079,7 +5652,7 @@ async function openUrl(url) {
|
|
|
5079
5652
|
|
|
5080
5653
|
//#endregion
|
|
5081
5654
|
//#region src/utils/sponsors.ts
|
|
5082
|
-
const SPONSORS_JSON_URL = "https://
|
|
5655
|
+
const SPONSORS_JSON_URL = "https://better-fullstack-web.vercel.app/sponsors.json";
|
|
5083
5656
|
async function fetchSponsors(url = SPONSORS_JSON_URL) {
|
|
5084
5657
|
const s = spinner();
|
|
5085
5658
|
s.start("Fetching sponsors…");
|
|
@@ -5148,6 +5721,8 @@ const router = os.router({
|
|
|
5148
5721
|
animation: types_exports.AnimationSchema.optional(),
|
|
5149
5722
|
logging: types_exports.LoggingSchema.optional(),
|
|
5150
5723
|
observability: types_exports.ObservabilitySchema.optional(),
|
|
5724
|
+
cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
|
|
5725
|
+
caching: types_exports.CachingSchema.optional().describe("Caching solution"),
|
|
5151
5726
|
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
5152
5727
|
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
5153
5728
|
examples: z.array(types_exports.ExamplesSchema).optional(),
|
|
@@ -5190,7 +5765,7 @@ const router = os.router({
|
|
|
5190
5765
|
}
|
|
5191
5766
|
}),
|
|
5192
5767
|
docs: os.meta({ description: "Open Better Fullstack documentation" }).handler(async () => {
|
|
5193
|
-
const DOCS_URL = "https://better-
|
|
5768
|
+
const DOCS_URL = "https://better-fullstack-web.vercel.app/docs";
|
|
5194
5769
|
try {
|
|
5195
5770
|
await openUrl(DOCS_URL);
|
|
5196
5771
|
log.success(pc.blue("Opened docs in your default browser."));
|
|
@@ -5199,7 +5774,7 @@ const router = os.router({
|
|
|
5199
5774
|
}
|
|
5200
5775
|
}),
|
|
5201
5776
|
builder: os.meta({ description: "Open the web-based stack builder" }).handler(async () => {
|
|
5202
|
-
const BUILDER_URL = "https://better-
|
|
5777
|
+
const BUILDER_URL = "https://better-fullstack-web.vercel.app/new";
|
|
5203
5778
|
try {
|
|
5204
5779
|
await openUrl(BUILDER_URL);
|
|
5205
5780
|
log.success(pc.blue("Opened builder in your default browser."));
|
|
@@ -5212,7 +5787,7 @@ const caller = createRouterClient(router, { context: {} });
|
|
|
5212
5787
|
function createBtsCli() {
|
|
5213
5788
|
return createCli({
|
|
5214
5789
|
router,
|
|
5215
|
-
name: "create-better-
|
|
5790
|
+
name: "create-better-fullstack",
|
|
5216
5791
|
version: getLatestCLIVersion()
|
|
5217
5792
|
});
|
|
5218
5793
|
}
|
|
@@ -5222,7 +5797,7 @@ function createBtsCli() {
|
|
|
5222
5797
|
*
|
|
5223
5798
|
* @example
|
|
5224
5799
|
* ```typescript
|
|
5225
|
-
* import { create } from "create-better-
|
|
5800
|
+
* import { create } from "create-better-fullstack";
|
|
5226
5801
|
*
|
|
5227
5802
|
* const result = await create("my-app", {
|
|
5228
5803
|
* frontend: ["tanstack-router"],
|
|
@@ -5277,7 +5852,7 @@ async function builder() {
|
|
|
5277
5852
|
*
|
|
5278
5853
|
* @example
|
|
5279
5854
|
* ```typescript
|
|
5280
|
-
* import { createVirtual, EMBEDDED_TEMPLATES } from "create-better-
|
|
5855
|
+
* import { createVirtual, EMBEDDED_TEMPLATES } from "create-better-fullstack";
|
|
5281
5856
|
*
|
|
5282
5857
|
* const result = await createVirtual({
|
|
5283
5858
|
* frontend: ["tanstack-router"],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "A CLI-first toolkit for building Full Stack applications. Skip the configuration. Ship the code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
7
7
|
"better-fullstack",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"type-safety",
|
|
29
29
|
"typescript"
|
|
30
30
|
],
|
|
31
|
-
"homepage": "https://
|
|
31
|
+
"homepage": "https://better-fullstack-web.vercel.app/",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"author": "Marve10s",
|
|
34
34
|
"repository": {
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"prepublishOnly": "npm run build"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@better-fullstack/template-generator": "1.
|
|
76
|
-
"@better-fullstack/types": "1.
|
|
75
|
+
"@better-fullstack/template-generator": "^1.1.0",
|
|
76
|
+
"@better-fullstack/types": "^1.1.0",
|
|
77
77
|
"@clack/core": "^0.5.0",
|
|
78
78
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
79
79
|
"@orpc/server": "^1.13.0",
|