create-better-t-stack 3.7.3-canary.8e47571f → 3.7.3-canary.8e4d5716

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/package.json +19 -23
  2. package/src/cli.ts +3 -0
  3. package/src/constants.ts +188 -0
  4. package/src/helpers/addons/addons-setup.ts +226 -0
  5. package/src/helpers/addons/examples-setup.ts +104 -0
  6. package/src/helpers/addons/fumadocs-setup.ts +103 -0
  7. package/src/helpers/addons/ruler-setup.ts +139 -0
  8. package/src/helpers/addons/starlight-setup.ts +51 -0
  9. package/src/helpers/addons/tauri-setup.ts +96 -0
  10. package/src/helpers/addons/ultracite-setup.ts +232 -0
  11. package/src/helpers/addons/vite-pwa-setup.ts +59 -0
  12. package/src/helpers/core/add-addons.ts +85 -0
  13. package/src/helpers/core/add-deployment.ts +102 -0
  14. package/src/helpers/core/api-setup.ts +280 -0
  15. package/src/helpers/core/auth-setup.ts +203 -0
  16. package/src/helpers/core/backend-setup.ts +73 -0
  17. package/src/helpers/core/command-handlers.ts +354 -0
  18. package/src/helpers/core/convex-codegen.ts +14 -0
  19. package/src/helpers/core/create-project.ts +133 -0
  20. package/src/helpers/core/create-readme.ts +687 -0
  21. package/src/helpers/core/db-setup.ts +184 -0
  22. package/src/helpers/core/detect-project-config.ts +41 -0
  23. package/src/helpers/core/env-setup.ts +449 -0
  24. package/src/helpers/core/git.ts +31 -0
  25. package/src/helpers/core/install-dependencies.ts +32 -0
  26. package/src/helpers/core/payments-setup.ts +48 -0
  27. package/src/helpers/core/post-installation.ts +383 -0
  28. package/src/helpers/core/project-config.ts +246 -0
  29. package/src/helpers/core/runtime-setup.ts +76 -0
  30. package/src/helpers/core/template-manager.ts +917 -0
  31. package/src/helpers/core/workspace-setup.ts +184 -0
  32. package/src/helpers/database-providers/d1-setup.ts +28 -0
  33. package/src/helpers/database-providers/docker-compose-setup.ts +50 -0
  34. package/src/helpers/database-providers/mongodb-atlas-setup.ts +186 -0
  35. package/src/helpers/database-providers/neon-setup.ts +243 -0
  36. package/src/helpers/database-providers/planetscale-setup.ts +78 -0
  37. package/src/helpers/database-providers/prisma-postgres-setup.ts +196 -0
  38. package/src/helpers/database-providers/supabase-setup.ts +218 -0
  39. package/src/helpers/database-providers/turso-setup.ts +309 -0
  40. package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +80 -0
  41. package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +51 -0
  42. package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +104 -0
  43. package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +32 -0
  44. package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +32 -0
  45. package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +98 -0
  46. package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +33 -0
  47. package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +98 -0
  48. package/src/helpers/deployment/alchemy/env-dts-setup.ts +76 -0
  49. package/src/helpers/deployment/alchemy/index.ts +7 -0
  50. package/src/helpers/deployment/server-deploy-setup.ts +55 -0
  51. package/src/helpers/deployment/web-deploy-setup.ts +58 -0
  52. package/src/index.ts +253 -0
  53. package/src/prompts/addons.ts +178 -0
  54. package/src/prompts/api.ts +49 -0
  55. package/src/prompts/auth.ts +84 -0
  56. package/src/prompts/backend.ts +83 -0
  57. package/src/prompts/config-prompts.ts +138 -0
  58. package/src/prompts/database-setup.ts +112 -0
  59. package/src/prompts/database.ts +57 -0
  60. package/src/prompts/examples.ts +64 -0
  61. package/src/prompts/frontend.ts +118 -0
  62. package/src/prompts/git.ts +16 -0
  63. package/src/prompts/install.ts +16 -0
  64. package/src/prompts/orm.ts +53 -0
  65. package/src/prompts/package-manager.ts +32 -0
  66. package/src/prompts/payments.ts +50 -0
  67. package/src/prompts/project-name.ts +86 -0
  68. package/src/prompts/runtime.ts +47 -0
  69. package/src/prompts/server-deploy.ts +91 -0
  70. package/src/prompts/web-deploy.ts +107 -0
  71. package/src/types.ts +2 -0
  72. package/src/utils/add-package-deps.ts +57 -0
  73. package/src/utils/analytics.ts +39 -0
  74. package/src/utils/better-auth-plugin-setup.ts +71 -0
  75. package/src/utils/biome-formatter.ts +82 -0
  76. package/src/utils/bts-config.ts +122 -0
  77. package/src/utils/command-exists.ts +16 -0
  78. package/src/utils/compatibility-rules.ts +319 -0
  79. package/src/utils/compatibility.ts +11 -0
  80. package/src/utils/config-processing.ts +130 -0
  81. package/src/utils/config-validation.ts +470 -0
  82. package/src/utils/display-config.ts +96 -0
  83. package/src/utils/docker-utils.ts +70 -0
  84. package/src/utils/errors.ts +32 -0
  85. package/src/utils/generate-reproducible-command.ts +53 -0
  86. package/src/utils/get-latest-cli-version.ts +11 -0
  87. package/src/utils/get-package-manager.ts +13 -0
  88. package/src/utils/open-url.ts +25 -0
  89. package/src/utils/package-runner.ts +23 -0
  90. package/src/utils/project-directory.ts +102 -0
  91. package/src/utils/project-name-validation.ts +43 -0
  92. package/src/utils/render-title.ts +48 -0
  93. package/src/utils/setup-catalogs.ts +192 -0
  94. package/src/utils/sponsors.ts +101 -0
  95. package/src/utils/telemetry.ts +19 -0
  96. package/src/utils/template-processor.ts +64 -0
  97. package/src/utils/templates.ts +94 -0
  98. package/src/utils/ts-morph.ts +26 -0
  99. package/src/validation.ts +117 -0
  100. package/dist/cli.d.mts +0 -1
  101. package/dist/cli.mjs +0 -8
  102. package/dist/index.d.mts +0 -347
  103. package/dist/index.mjs +0 -4
  104. package/dist/src-CxVxLS85.mjs +0 -7077
@@ -0,0 +1,687 @@
1
+ import path from "node:path";
2
+ import consola from "consola";
3
+ import fs from "fs-extra";
4
+ import type {
5
+ Addons,
6
+ API,
7
+ Auth,
8
+ Database,
9
+ DatabaseSetup,
10
+ Frontend,
11
+ ORM,
12
+ ProjectConfig,
13
+ Runtime,
14
+ } from "../../types";
15
+
16
+ export async function createReadme(projectDir: string, options: ProjectConfig) {
17
+ const readmePath = path.join(projectDir, "README.md");
18
+ const content = generateReadmeContent(options);
19
+
20
+ try {
21
+ await fs.writeFile(readmePath, content);
22
+ } catch (error) {
23
+ consola.error("Failed to create README.md file:", error);
24
+ }
25
+ }
26
+
27
+ function generateReadmeContent(options: ProjectConfig) {
28
+ const {
29
+ projectName,
30
+ packageManager,
31
+ database,
32
+ auth,
33
+ addons = [],
34
+ orm = "drizzle",
35
+ runtime = "bun",
36
+ frontend = ["tanstack-router"],
37
+ backend = "hono",
38
+ api = "trpc",
39
+ webDeploy,
40
+ serverDeploy,
41
+ } = options;
42
+
43
+ const isConvex = backend === "convex";
44
+ const hasReactRouter = frontend.includes("react-router");
45
+ const hasNative =
46
+ frontend.includes("native-bare") ||
47
+ frontend.includes("native-uniwind") ||
48
+ frontend.includes("native-unistyles");
49
+ const hasSvelte = frontend.includes("svelte");
50
+
51
+ const packageManagerRunCmd = `${packageManager} run`;
52
+
53
+ let webPort = "3001";
54
+ if (hasReactRouter || hasSvelte) {
55
+ webPort = "5173";
56
+ }
57
+
58
+ const stackDescription = generateStackDescription(frontend, backend, api, isConvex);
59
+
60
+ return `# ${projectName}
61
+
62
+ This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack${
63
+ stackDescription ? ` that combines ${stackDescription}` : ""
64
+ }.
65
+
66
+ ## Features
67
+
68
+ ${generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api)}
69
+
70
+ ## Getting Started
71
+
72
+ First, install the dependencies:
73
+
74
+ \`\`\`bash
75
+ ${packageManager} install
76
+ \`\`\`
77
+ ${
78
+ isConvex
79
+ ? `
80
+ ## Convex Setup
81
+
82
+ This project uses Convex as a backend. You'll need to set up Convex before running the app:
83
+
84
+ \`\`\`bash
85
+ ${packageManagerRunCmd} dev:setup
86
+ \`\`\`
87
+
88
+ Follow the prompts to create a new Convex project and connect it to your application.${
89
+ auth === "clerk"
90
+ ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup."
91
+ : ""
92
+ }`
93
+ : generateDatabaseSetup(
94
+ database,
95
+ auth,
96
+ packageManagerRunCmd,
97
+ orm,
98
+ options.dbSetup,
99
+ options.serverDeploy,
100
+ options.backend,
101
+ )
102
+ }
103
+
104
+ Then, run the development server:
105
+
106
+ \`\`\`bash
107
+ ${packageManagerRunCmd} dev
108
+ \`\`\`
109
+
110
+ ${generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex)}
111
+
112
+ ${
113
+ addons.includes("pwa") && hasReactRouter
114
+ ? "\n## PWA Support with React Router v7\n\nThere is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809\n"
115
+ : ""
116
+ }
117
+
118
+ ${generateWorkersRuntimeNotes(runtime)}
119
+
120
+ ${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
121
+
122
+ ## Project Structure
123
+
124
+ \`\`\`
125
+ ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth)}
126
+ \`\`\`
127
+
128
+ ## Available Scripts
129
+
130
+ ${generateScriptsList(packageManagerRunCmd, database, orm, auth, hasNative, addons, backend)}
131
+ `;
132
+ }
133
+
134
+ function generateStackDescription(
135
+ frontend: Frontend[],
136
+ backend: string,
137
+ api: API,
138
+ isConvex: boolean,
139
+ ) {
140
+ const parts: string[] = [];
141
+
142
+ const hasTanstackRouter = frontend.includes("tanstack-router");
143
+ const hasReactRouter = frontend.includes("react-router");
144
+ const hasNext = frontend.includes("next");
145
+ const hasTanstackStart = frontend.includes("tanstack-start");
146
+ const hasSvelte = frontend.includes("svelte");
147
+ const hasNuxt = frontend.includes("nuxt");
148
+ const hasSolid = frontend.includes("solid");
149
+ const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
150
+
151
+ if (!hasFrontendNone) {
152
+ if (hasTanstackRouter) {
153
+ parts.push("React, TanStack Router");
154
+ } else if (hasReactRouter) {
155
+ parts.push("React, React Router");
156
+ } else if (hasNext) {
157
+ parts.push("Next.js");
158
+ } else if (hasTanstackStart) {
159
+ parts.push("React, TanStack Start");
160
+ } else if (hasSvelte) {
161
+ parts.push("SvelteKit");
162
+ } else if (hasNuxt) {
163
+ parts.push("Nuxt");
164
+ } else if (hasSolid) {
165
+ parts.push("SolidJS");
166
+ }
167
+ }
168
+
169
+ if (backend !== "none") {
170
+ parts.push(backend[0].toUpperCase() + backend.slice(1));
171
+ }
172
+
173
+ if (!isConvex && api !== "none") {
174
+ parts.push(api.toUpperCase());
175
+ }
176
+
177
+ return parts.length > 0 ? `${parts.join(", ")}, and more` : "";
178
+ }
179
+
180
+ function generateRunningInstructions(
181
+ frontend: Frontend[],
182
+ backend: string,
183
+ webPort: string,
184
+ hasNative: boolean,
185
+ isConvex: boolean,
186
+ ) {
187
+ const instructions: string[] = [];
188
+
189
+ const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
190
+ const isBackendNone = backend === "none";
191
+ const isBackendSelf = backend === "self";
192
+
193
+ if (!hasFrontendNone) {
194
+ const hasTanstackRouter = frontend.includes("tanstack-router");
195
+ const hasReactRouter = frontend.includes("react-router");
196
+ const hasNext = frontend.includes("next");
197
+ const hasTanstackStart = frontend.includes("tanstack-start");
198
+ const hasSvelte = frontend.includes("svelte");
199
+ const hasNuxt = frontend.includes("nuxt");
200
+ const hasSolid = frontend.includes("solid");
201
+
202
+ if (
203
+ hasTanstackRouter ||
204
+ hasReactRouter ||
205
+ hasNext ||
206
+ hasTanstackStart ||
207
+ hasSvelte ||
208
+ hasNuxt ||
209
+ hasSolid
210
+ ) {
211
+ if (isBackendSelf) {
212
+ instructions.push(
213
+ `Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see your fullstack application.`,
214
+ );
215
+ } else {
216
+ instructions.push(
217
+ `Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`,
218
+ );
219
+ }
220
+ }
221
+ }
222
+
223
+ if (hasNative) {
224
+ instructions.push("Use the Expo Go app to run the mobile application.");
225
+ }
226
+
227
+ if (isConvex) {
228
+ instructions.push("Your app will connect to the Convex cloud backend automatically.");
229
+ } else if (!isBackendNone && !isBackendSelf) {
230
+ instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
231
+ }
232
+
233
+ return instructions.join("\n");
234
+ }
235
+
236
+ function generateProjectStructure(
237
+ projectName: string,
238
+ frontend: Frontend[],
239
+ backend: string,
240
+ addons: Addons[],
241
+ isConvex: boolean,
242
+ api: API,
243
+ auth: Auth,
244
+ ) {
245
+ const structure: string[] = [`${projectName}/`, "├── apps/"];
246
+
247
+ const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
248
+ const isBackendNone = backend === "none";
249
+ const isBackendSelf = backend === "self";
250
+
251
+ if (!hasFrontendNone) {
252
+ const hasTanstackRouter = frontend.includes("tanstack-router");
253
+ const hasReactRouter = frontend.includes("react-router");
254
+ const hasNext = frontend.includes("next");
255
+ const hasTanstackStart = frontend.includes("tanstack-start");
256
+ const hasSvelte = frontend.includes("svelte");
257
+ const hasNuxt = frontend.includes("nuxt");
258
+ const hasSolid = frontend.includes("solid");
259
+
260
+ if (
261
+ hasTanstackRouter ||
262
+ hasReactRouter ||
263
+ hasNext ||
264
+ hasTanstackStart ||
265
+ hasSvelte ||
266
+ hasNuxt ||
267
+ hasSolid
268
+ ) {
269
+ let frontendType = "";
270
+ if (hasTanstackRouter) frontendType = "React + TanStack Router";
271
+ else if (hasReactRouter) frontendType = "React + React Router";
272
+ else if (hasNext) frontendType = "Next.js";
273
+ else if (hasTanstackStart) frontendType = "React + TanStack Start";
274
+ else if (hasSvelte) frontendType = "SvelteKit";
275
+ else if (hasNuxt) frontendType = "Nuxt";
276
+ else if (hasSolid) frontendType = "SolidJS";
277
+
278
+ if (isBackendSelf) {
279
+ structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
280
+ } else {
281
+ structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
282
+ }
283
+ }
284
+ }
285
+
286
+ const hasNative =
287
+ frontend.includes("native-bare") ||
288
+ frontend.includes("native-uniwind") ||
289
+ frontend.includes("native-unistyles");
290
+ if (hasNative) {
291
+ if (isBackendSelf) {
292
+ structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
293
+ } else {
294
+ structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
295
+ }
296
+ }
297
+
298
+ if (addons.includes("starlight")) {
299
+ if (isBackendSelf) {
300
+ structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
301
+ } else {
302
+ structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
303
+ }
304
+ }
305
+
306
+ if (!isBackendSelf && !isBackendNone && !isConvex) {
307
+ const backendName = backend[0].toUpperCase() + backend.slice(1);
308
+ const apiName = api !== "none" ? api.toUpperCase() : "";
309
+ const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
310
+ structure.push(`│ └── server/ # Backend API (${backendDesc})`);
311
+ }
312
+
313
+ if (isConvex || !isBackendNone) {
314
+ structure.push("├── packages/");
315
+
316
+ if (isConvex) {
317
+ structure.push("│ ├── backend/ # Convex backend functions and schema");
318
+ if (auth === "clerk") {
319
+ structure.push(
320
+ "│ │ ├── convex/ # Convex functions and schema",
321
+ "│ │ └── .env.local # Convex environment variables",
322
+ );
323
+ }
324
+ }
325
+
326
+ if (!isConvex) {
327
+ structure.push("│ ├── api/ # API layer / business logic");
328
+
329
+ if (auth !== "none") {
330
+ structure.push("│ ├── auth/ # Authentication configuration & logic");
331
+ }
332
+
333
+ if (api !== "none" || auth !== "none") {
334
+ structure.push("│ └── db/ # Database schema & queries");
335
+ }
336
+ }
337
+ }
338
+
339
+ return structure.join("\n");
340
+ }
341
+
342
+ function generateFeaturesList(
343
+ database: Database,
344
+ auth: Auth,
345
+ addons: Addons[],
346
+ orm: ORM,
347
+ runtime: Runtime,
348
+ frontend: Frontend[],
349
+ backend: string,
350
+ api: API,
351
+ ) {
352
+ const isConvex = backend === "convex";
353
+ const isBackendNone = backend === "none";
354
+ const hasTanstackRouter = frontend.includes("tanstack-router");
355
+ const hasReactRouter = frontend.includes("react-router");
356
+ const hasNative =
357
+ frontend.includes("native-bare") ||
358
+ frontend.includes("native-uniwind") ||
359
+ frontend.includes("native-unistyles");
360
+ const hasNext = frontend.includes("next");
361
+ const hasTanstackStart = frontend.includes("tanstack-start");
362
+ const hasSvelte = frontend.includes("svelte");
363
+ const hasNuxt = frontend.includes("nuxt");
364
+ const hasSolid = frontend.includes("solid");
365
+ const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
366
+
367
+ const addonsList = ["- **TypeScript** - For type safety and improved developer experience"];
368
+
369
+ if (!hasFrontendNone) {
370
+ if (hasTanstackRouter) {
371
+ addonsList.push("- **TanStack Router** - File-based routing with full type safety");
372
+ } else if (hasReactRouter) {
373
+ addonsList.push("- **React Router** - Declarative routing for React");
374
+ } else if (hasNext) {
375
+ addonsList.push("- **Next.js** - Full-stack React framework");
376
+ } else if (hasTanstackStart) {
377
+ addonsList.push("- **TanStack Start** - SSR framework with TanStack Router");
378
+ } else if (hasSvelte) {
379
+ addonsList.push("- **SvelteKit** - Web framework for building Svelte apps");
380
+ } else if (hasNuxt) {
381
+ addonsList.push("- **Nuxt** - The Intuitive Vue Framework");
382
+ } else if (hasSolid) {
383
+ addonsList.push("- **SolidJS** - Simple and performant reactivity");
384
+ }
385
+ }
386
+
387
+ if (hasNative) {
388
+ addonsList.push("- **React Native** - Build mobile apps using React");
389
+ addonsList.push("- **Expo** - Tools for React Native development");
390
+ }
391
+
392
+ if (!hasFrontendNone) {
393
+ addonsList.push(
394
+ "- **TailwindCSS** - Utility-first CSS for rapid UI development",
395
+ "- **shadcn/ui** - Reusable UI components",
396
+ );
397
+ }
398
+
399
+ if (isConvex) {
400
+ addonsList.push("- **Convex** - Reactive backend-as-a-service platform");
401
+ } else if (!isBackendNone) {
402
+ if (backend === "hono") {
403
+ addonsList.push("- **Hono** - Lightweight, performant server framework");
404
+ } else if (backend === "express") {
405
+ addonsList.push("- **Express** - Fast, unopinionated web framework");
406
+ } else if (backend === "fastify") {
407
+ addonsList.push("- **Fastify** - Fast, low-overhead web framework");
408
+ } else if (backend === "elysia") {
409
+ addonsList.push("- **Elysia** - Type-safe, high-performance framework");
410
+ }
411
+
412
+ if (api === "trpc") {
413
+ addonsList.push("- **tRPC** - End-to-end type-safe APIs");
414
+ } else if (api === "orpc") {
415
+ addonsList.push("- **oRPC** - End-to-end type-safe APIs with OpenAPI integration");
416
+ }
417
+
418
+ if (runtime !== "none") {
419
+ addonsList.push(
420
+ `- **${
421
+ runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime
422
+ }** - Runtime environment`,
423
+ );
424
+ }
425
+ }
426
+
427
+ if (database !== "none" && !isConvex) {
428
+ const ormName =
429
+ orm === "drizzle"
430
+ ? "Drizzle"
431
+ : orm === "prisma"
432
+ ? "Prisma"
433
+ : orm === "mongoose"
434
+ ? "Mongoose"
435
+ : "ORM";
436
+ const dbName =
437
+ database === "sqlite"
438
+ ? "SQLite/Turso"
439
+ : database === "postgres"
440
+ ? "PostgreSQL"
441
+ : database === "mysql"
442
+ ? "MySQL"
443
+ : database === "mongodb"
444
+ ? "MongoDB"
445
+ : "Database";
446
+
447
+ addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
448
+ }
449
+
450
+ if (auth !== "none") {
451
+ const authLabel = auth === "clerk" ? "Clerk" : "Better-Auth";
452
+ addonsList.push(`- **Authentication** - ${authLabel}`);
453
+ }
454
+
455
+ for (const addon of addons) {
456
+ if (addon === "pwa") {
457
+ addonsList.push("- **PWA** - Progressive Web App support");
458
+ } else if (addon === "tauri") {
459
+ addonsList.push("- **Tauri** - Build native desktop applications");
460
+ } else if (addon === "biome") {
461
+ addonsList.push("- **Biome** - Linting and formatting");
462
+ } else if (addon === "husky") {
463
+ addonsList.push("- **Husky** - Git hooks for code quality");
464
+ } else if (addon === "starlight") {
465
+ addonsList.push("- **Starlight** - Documentation site with Astro");
466
+ } else if (addon === "turborepo") {
467
+ addonsList.push("- **Turborepo** - Optimized monorepo build system");
468
+ }
469
+ }
470
+
471
+ return addonsList.join("\n");
472
+ }
473
+
474
+ function generateDatabaseSetup(
475
+ database: Database,
476
+ _auth: Auth,
477
+ packageManagerRunCmd: string,
478
+ orm: ORM,
479
+ dbSetup: DatabaseSetup,
480
+ _serverDeploy?: string,
481
+ backend?: string,
482
+ ) {
483
+ if (database === "none") {
484
+ return "";
485
+ }
486
+
487
+ const isBackendSelf = backend === "self";
488
+ const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
489
+ const dbLocalPath = "packages/db";
490
+
491
+ let setup = "## Database Setup\n\n";
492
+
493
+ if (database === "sqlite") {
494
+ setup += `This project uses SQLite${
495
+ orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`
496
+ }.
497
+
498
+ 1. Start the local SQLite database:
499
+ ${
500
+ dbSetup === "d1"
501
+ ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy."
502
+ : `\`\`\`bash
503
+ cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
504
+ \`\`\`
505
+ `
506
+ }
507
+
508
+ 2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
509
+ `;
510
+ } else if (database === "postgres") {
511
+ setup += `This project uses PostgreSQL${
512
+ orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`
513
+ }.
514
+
515
+ 1. Make sure you have a PostgreSQL database set up.
516
+ 2. Update your \`${envPath}\` file with your PostgreSQL connection details.
517
+ `;
518
+ } else if (database === "mysql") {
519
+ setup += `This project uses MySQL${
520
+ orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`
521
+ }.
522
+
523
+ 1. Make sure you have a MySQL database set up.
524
+ 2. Update your \`${envPath}\` file with your MySQL connection details.
525
+ `;
526
+ } else if (database === "mongodb") {
527
+ setup += `This project uses MongoDB ${
528
+ orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`
529
+ }.
530
+
531
+ 1. Make sure you have MongoDB set up.
532
+ 2. Update your \`${envPath}\` file with your MongoDB connection URI.
533
+ `;
534
+ }
535
+
536
+ setup += `
537
+ 3. ${
538
+ orm === "prisma"
539
+ ? `Generate the Prisma client and push the schema:
540
+ \`\`\`bash
541
+ ${packageManagerRunCmd} db:push
542
+ \`\`\``
543
+ : orm === "drizzle"
544
+ ? `Apply the schema to your database:
545
+ \`\`\`bash
546
+ ${packageManagerRunCmd} db:push
547
+ \`\`\``
548
+ : `Apply the schema to your database:
549
+ \`\`\`bash
550
+ ${packageManagerRunCmd} db:push
551
+ \`\`\``
552
+ }
553
+ `;
554
+
555
+ return setup;
556
+ }
557
+
558
+ function generateScriptsList(
559
+ packageManagerRunCmd: string,
560
+ database: Database,
561
+ orm: ORM,
562
+ _auth: Auth,
563
+ hasNative: boolean,
564
+ addons: Addons[],
565
+ backend: string,
566
+ ) {
567
+ const isConvex = backend === "convex";
568
+ const isBackendNone = backend === "none";
569
+ const isBackendSelf = backend === "self";
570
+
571
+ let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
572
+ - \`${packageManagerRunCmd} build\`: Build all applications`;
573
+
574
+ if (!isBackendSelf) {
575
+ scripts += `
576
+ - \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
577
+ }
578
+
579
+ if (isConvex) {
580
+ scripts += `
581
+ - \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
582
+ } else if (!isBackendNone && !isBackendSelf) {
583
+ scripts += `
584
+ - \`${packageManagerRunCmd} dev:server\`: Start only the server`;
585
+ }
586
+
587
+ scripts += `
588
+ - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
589
+
590
+ if (hasNative) {
591
+ scripts += `
592
+ - \`${packageManagerRunCmd} dev:native\`: Start the React Native/Expo development server`;
593
+ }
594
+
595
+ if (database !== "none" && !isConvex) {
596
+ scripts += `
597
+ - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
598
+ - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
599
+
600
+ if (database === "sqlite" && orm === "drizzle") {
601
+ scripts += `
602
+ - \`cd packages/db && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
603
+ }
604
+ }
605
+
606
+ if (addons.includes("biome")) {
607
+ scripts += `
608
+ - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
609
+ }
610
+
611
+ if (addons.includes("pwa")) {
612
+ scripts += `
613
+ - \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`;
614
+ }
615
+
616
+ if (addons.includes("tauri")) {
617
+ scripts += `
618
+ - \`cd apps/web && ${packageManagerRunCmd} desktop:dev\`: Start Tauri desktop app in development
619
+ - \`cd apps/web && ${packageManagerRunCmd} desktop:build\`: Build Tauri desktop app`;
620
+ }
621
+
622
+ if (addons.includes("starlight")) {
623
+ scripts += `
624
+ - \`cd apps/docs && ${packageManagerRunCmd} dev\`: Start documentation site
625
+ - \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
626
+ }
627
+
628
+ return scripts;
629
+ }
630
+
631
+ function generateWorkersRuntimeNotes(runtime: Runtime) {
632
+ if (runtime !== "workers") {
633
+ return "";
634
+ }
635
+
636
+ return `
637
+ ## Before Deploying to Cloudflare
638
+
639
+ When you are ready to deploy your app to Cloudflare Workers, you'll have to make a couple changes.
640
+ - Change your url environment variables to match your \`*.workers.dev\` domains generated by Cloudflare:
641
+
642
+ \`\`\`bash
643
+ # apps/web/.env
644
+ SERVER_URL={your-production-server-domain}
645
+
646
+ # apps/server/.env
647
+ CORS_ORIGIN={your-production-web-domain}
648
+ BETTER_AUTH_URL={your-production-server-domain}
649
+ \`\`\`
650
+ - In \`apps/server/src/lib/auth.ts\`, uncomment the \`session.cookieCache\` and \`advanced.crossSubDomainCookies\` sections and replace \`<your-workers-subdomain>\` with your actual workers subdomain. These settings are required to ensure cookies are transferred properly between your web and server domains.
651
+ `;
652
+ }
653
+
654
+ function generateDeploymentCommands(
655
+ packageManagerRunCmd: string,
656
+ webDeploy?: string,
657
+ serverDeploy?: string,
658
+ ) {
659
+ const lines: string[] = [];
660
+
661
+ if (webDeploy === "alchemy" || serverDeploy === "alchemy") {
662
+ lines.push("## Deployment (Alchemy)");
663
+ if (webDeploy === "alchemy" && serverDeploy !== "alchemy") {
664
+ lines.push(
665
+ `- Web dev: cd apps/web && ${packageManagerRunCmd} dev`,
666
+ `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`,
667
+ `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`,
668
+ );
669
+ }
670
+ if (serverDeploy === "alchemy" && webDeploy !== "alchemy") {
671
+ lines.push(
672
+ `- Server dev: cd apps/server && ${packageManagerRunCmd} dev`,
673
+ `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`,
674
+ `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`,
675
+ );
676
+ }
677
+ if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
678
+ lines.push(
679
+ `- Dev: ${packageManagerRunCmd} dev`,
680
+ `- Deploy: ${packageManagerRunCmd} deploy`,
681
+ `- Destroy: ${packageManagerRunCmd} destroy`,
682
+ );
683
+ }
684
+ }
685
+
686
+ return lines.length ? `\n${lines.join("\n")}\n` : "";
687
+ }