create-better-t-stack 2.50.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +8 -9
- package/dist/index.js +1 -1
- package/dist/{src-B8TD9m4n.js → src-BywywK-y.js} +825 -418
- package/package.json +2 -1
- package/templates/addons/ruler/.ruler/bts.md.hbs +24 -14
- package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +50 -0
- package/templates/api/orpc/native/utils/orpc.ts.hbs +5 -1
- package/templates/api/orpc/server/_gitignore +34 -0
- package/templates/api/orpc/server/package.json.hbs +22 -0
- package/templates/api/orpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
- package/templates/{backend/server/server-base → api/orpc/server}/src/routers/index.ts.hbs +2 -2
- package/templates/api/orpc/server/tsconfig.json.hbs +10 -0
- package/templates/api/orpc/server/tsdown.config.ts.hbs +7 -0
- package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +4 -2
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
- package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
- package/templates/api/trpc/native/utils/trpc.ts.hbs +5 -1
- package/templates/api/trpc/server/_gitignore +34 -0
- package/templates/api/trpc/server/package.json.hbs +21 -0
- package/templates/api/trpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
- package/templates/api/trpc/server/src/routers/index.ts.hbs +55 -0
- package/templates/api/trpc/server/tsconfig.json.hbs +10 -0
- package/templates/api/trpc/server/tsdown.config.ts.hbs +7 -0
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
- package/templates/auth/better-auth/{server/next/src/app/api/auth/[...all]/route.ts → fullstack/next/src/app/api/auth/[...all]/route.ts.hbs} +1 -1
- package/templates/auth/better-auth/server/base/_gitignore +34 -0
- package/templates/auth/better-auth/server/base/package.json.hbs +21 -0
- package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +12 -12
- package/templates/auth/better-auth/server/base/tsconfig.json.hbs +10 -0
- package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
- package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +8 -17
- package/templates/backend/server/{server-base → base}/package.json.hbs +1 -5
- package/templates/backend/server/base/tsconfig.json.hbs +13 -0
- package/templates/backend/server/base/tsdown.config.ts.hbs +9 -0
- package/templates/backend/server/elysia/src/index.ts.hbs +6 -6
- package/templates/backend/server/express/src/index.ts.hbs +6 -6
- package/templates/backend/server/fastify/src/index.ts.hbs +6 -6
- package/templates/backend/server/hono/src/index.ts.hbs +7 -7
- package/templates/base/_gitignore +47 -1
- package/templates/base/package.json.hbs +1 -3
- package/templates/{backend/server/server-base/tsconfig.json.hbs → base/tsconfig.base.json.hbs} +14 -13
- package/templates/base/tsconfig.json.hbs +3 -0
- package/templates/db/base/_gitignore +34 -0
- package/templates/db/base/package.json.hbs +21 -0
- package/templates/db/base/tsconfig.json.hbs +10 -0
- package/templates/db/base/tsdown.config.ts.hbs +7 -0
- package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +11 -2
- package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
- package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +11 -2
- package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
- package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +11 -2
- package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
- package/templates/db/prisma/mongodb/prisma.config.ts.hbs +9 -1
- package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
- package/templates/db/prisma/mysql/prisma.config.ts.hbs +9 -1
- package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
- package/templates/db/prisma/postgres/prisma.config.ts.hbs +11 -3
- package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
- package/templates/db/prisma/sqlite/prisma.config.ts.hbs +9 -1
- package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +3 -3
- package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
- package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +6 -6
- package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
- package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
- package/templates/extras/bunfig.toml.hbs +2 -2
- package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
- package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
- package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
- package/templates/frontend/react/next/package.json.hbs +1 -1
- package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
- package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
- package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
- package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
- package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
- package/templates/frontend/solid/tsconfig.json.hbs +1 -6
- package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
- package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +0 -52
- package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +0 -14
- package/templates/backend/server/next/next-env.d.ts +0 -5
- package/templates/backend/server/next/next.config.ts +0 -7
- package/templates/backend/server/next/package.json.hbs +0 -27
- package/templates/backend/server/next/src/app/route.ts +0 -5
- package/templates/backend/server/next/src/middleware.ts +0 -19
- package/templates/backend/server/next/tsconfig.json.hbs +0 -33
- package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
- package/templates/examples/ai/server/next/src/app/ai/route.ts.hbs +0 -15
- /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → src/index.ts.hbs} +0 -0
- /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → src/index.ts.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models/auth.model.ts → models/auth.model.ts.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
- /package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
- /package/templates/auth/better-auth/web/nuxt/app/middleware/{auth.ts → auth.ts.hbs} +0 -0
- /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
- /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
- /package/templates/examples/todo/server/drizzle/mysql/src/{db/schema → schema}/todo.ts +0 -0
- /package/templates/examples/todo/server/drizzle/postgres/src/{db/schema → schema}/todo.ts +0 -0
- /package/templates/examples/todo/server/drizzle/sqlite/src/{db/schema → schema}/todo.ts +0 -0
- /package/templates/examples/todo/server/mongoose/mongodb/src/{db/models/todo.model.ts → models/todo.model.ts.hbs} +0 -0
- /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
- /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
- /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
- /package/templates/examples/todo/server/prisma/sqlite/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
|
@@ -15,6 +15,7 @@ import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph"
|
|
|
15
15
|
import { glob } from "tinyglobby";
|
|
16
16
|
import handlebars from "handlebars";
|
|
17
17
|
import { Biome } from "@biomejs/js-api/nodejs";
|
|
18
|
+
import yaml from "yaml";
|
|
18
19
|
import os$1 from "node:os";
|
|
19
20
|
|
|
20
21
|
//#region src/utils/get-package-manager.ts
|
|
@@ -70,7 +71,7 @@ const dependencyVersionMap = {
|
|
|
70
71
|
"drizzle-orm": "^0.44.2",
|
|
71
72
|
"drizzle-kit": "^0.31.2",
|
|
72
73
|
"@planetscale/database": "^1.19.0",
|
|
73
|
-
"@libsql/client": "^0.
|
|
74
|
+
"@libsql/client": "^0.14.0",
|
|
74
75
|
"@neondatabase/serverless": "^1.0.1",
|
|
75
76
|
pg: "^8.14.1",
|
|
76
77
|
"@types/pg": "^8.11.11",
|
|
@@ -123,6 +124,7 @@ const dependencyVersionMap = {
|
|
|
123
124
|
"@trpc/tanstack-react-query": "^11.5.0",
|
|
124
125
|
"@trpc/server": "^11.5.0",
|
|
125
126
|
"@trpc/client": "^11.5.0",
|
|
127
|
+
next: "15.5.4",
|
|
126
128
|
convex: "^1.27.0",
|
|
127
129
|
"@convex-dev/react-query": "^0.0.0-alpha.8",
|
|
128
130
|
"convex-svelte": "^0.0.11",
|
|
@@ -146,7 +148,9 @@ const dependencyVersionMap = {
|
|
|
146
148
|
"@cloudflare/workers-types": "^4.20250822.0",
|
|
147
149
|
alchemy: "^0.70.0",
|
|
148
150
|
nitropack: "^2.12.4",
|
|
149
|
-
dotenv: "^17.2.
|
|
151
|
+
dotenv: "^17.2.2",
|
|
152
|
+
tsdown: "^0.15.5",
|
|
153
|
+
zod: "^4.1.11",
|
|
150
154
|
"@polar-sh/better-auth": "^1.1.3",
|
|
151
155
|
"@polar-sh/sdk": "^0.34.16"
|
|
152
156
|
};
|
|
@@ -195,9 +199,9 @@ const BackendSchema = z.enum([
|
|
|
195
199
|
"hono",
|
|
196
200
|
"express",
|
|
197
201
|
"fastify",
|
|
198
|
-
"next",
|
|
199
202
|
"elysia",
|
|
200
203
|
"convex",
|
|
204
|
+
"self",
|
|
201
205
|
"none"
|
|
202
206
|
]).describe("Backend framework");
|
|
203
207
|
const RuntimeSchema = z.enum([
|
|
@@ -343,6 +347,18 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
343
347
|
if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
|
|
344
348
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
345
349
|
}
|
|
350
|
+
const FULLSTACK_FRONTENDS$1 = ["next"];
|
|
351
|
+
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
352
|
+
const backend = config.backend || options.backend;
|
|
353
|
+
const frontends = config.frontend || options.frontend || [];
|
|
354
|
+
if (backend === "self") {
|
|
355
|
+
const { web, native } = splitFrontends(frontends);
|
|
356
|
+
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
|
|
357
|
+
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
358
|
+
}
|
|
359
|
+
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
360
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next or choose a different backend. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
|
|
361
|
+
}
|
|
346
362
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
347
363
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
|
|
348
364
|
if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
|
|
@@ -664,36 +680,34 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
|
664
680
|
|
|
665
681
|
//#endregion
|
|
666
682
|
//#region src/prompts/backend.ts
|
|
683
|
+
const FULLSTACK_FRONTENDS = ["next"];
|
|
667
684
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
668
685
|
if (backendFramework !== void 0) return backendFramework;
|
|
669
686
|
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
hint: "Ergonomic web framework for building backend servers"
|
|
695
|
-
}
|
|
696
|
-
];
|
|
687
|
+
const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
|
|
688
|
+
const backendOptions = [];
|
|
689
|
+
if (hasFullstackFrontend) backendOptions.push({
|
|
690
|
+
value: "self",
|
|
691
|
+
label: "Self (Fullstack)",
|
|
692
|
+
hint: "Use frontend's built-in api routes"
|
|
693
|
+
});
|
|
694
|
+
backendOptions.push({
|
|
695
|
+
value: "hono",
|
|
696
|
+
label: "Hono",
|
|
697
|
+
hint: "Lightweight, ultrafast web framework"
|
|
698
|
+
}, {
|
|
699
|
+
value: "express",
|
|
700
|
+
label: "Express",
|
|
701
|
+
hint: "Fast, unopinionated, minimalist web framework for Node.js"
|
|
702
|
+
}, {
|
|
703
|
+
value: "fastify",
|
|
704
|
+
label: "Fastify",
|
|
705
|
+
hint: "Fast, low-overhead web framework for Node.js"
|
|
706
|
+
}, {
|
|
707
|
+
value: "elysia",
|
|
708
|
+
label: "Elysia",
|
|
709
|
+
hint: "Ergonomic web framework for building backend servers"
|
|
710
|
+
});
|
|
697
711
|
if (!hasIncompatibleFrontend) backendOptions.push({
|
|
698
712
|
value: "convex",
|
|
699
713
|
label: "Convex",
|
|
@@ -707,7 +721,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
707
721
|
const response = await select({
|
|
708
722
|
message: "Select backend",
|
|
709
723
|
options: backendOptions,
|
|
710
|
-
initialValue: DEFAULT_CONFIG.backend
|
|
724
|
+
initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
|
|
711
725
|
});
|
|
712
726
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
713
727
|
return response;
|
|
@@ -1086,9 +1100,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
|
1086
1100
|
//#endregion
|
|
1087
1101
|
//#region src/prompts/runtime.ts
|
|
1088
1102
|
async function getRuntimeChoice(runtime, backend) {
|
|
1089
|
-
if (backend === "convex" || backend === "none") return "none";
|
|
1103
|
+
if (backend === "convex" || backend === "none" || backend === "self") return "none";
|
|
1090
1104
|
if (runtime !== void 0) return runtime;
|
|
1091
|
-
if (backend === "next") return "node";
|
|
1092
1105
|
const runtimeOptions = [{
|
|
1093
1106
|
value: "bun",
|
|
1094
1107
|
label: "Bun",
|
|
@@ -1754,6 +1767,12 @@ function validateBackendNoneConstraints(config, providedFlags) {
|
|
|
1754
1767
|
if (has("dbSetup") && config.dbSetup !== "none") exitWithError("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
|
|
1755
1768
|
if (has("serverDeploy") && config.serverDeploy !== "none") exitWithError("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
|
|
1756
1769
|
}
|
|
1770
|
+
function validateSelfBackendConstraints(config, providedFlags) {
|
|
1771
|
+
const { backend } = config;
|
|
1772
|
+
if (backend !== "self") return;
|
|
1773
|
+
const has = (k) => providedFlags.has(k);
|
|
1774
|
+
if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'self' (fullstack) requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
|
|
1775
|
+
}
|
|
1757
1776
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
1758
1777
|
const { backend } = config;
|
|
1759
1778
|
if (config.auth === "clerk" && backend !== "convex") exitWithError("Clerk authentication is only supported with the Convex backend. Please use '--backend convex' or choose a different auth provider.");
|
|
@@ -1765,8 +1784,8 @@ function validateBackendConstraints(config, providedFlags, options) {
|
|
|
1765
1784
|
].includes(f));
|
|
1766
1785
|
if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
|
|
1767
1786
|
}
|
|
1768
|
-
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
|
|
1769
|
-
if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex' or '--backend
|
|
1787
|
+
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
|
|
1788
|
+
if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex', '--backend none', or '--backend self'. Please choose 'bun', 'node', or remove the --runtime flag.");
|
|
1770
1789
|
}
|
|
1771
1790
|
if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
|
|
1772
1791
|
const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
|
|
@@ -1792,10 +1811,12 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1792
1811
|
validateDatabaseSetup(config, providedFlags);
|
|
1793
1812
|
validateConvexConstraints(config, providedFlags);
|
|
1794
1813
|
validateBackendNoneConstraints(config, providedFlags);
|
|
1814
|
+
validateSelfBackendConstraints(config, providedFlags);
|
|
1795
1815
|
validateBackendConstraints(config, providedFlags, options);
|
|
1796
1816
|
validateFrontendConstraints(config, providedFlags);
|
|
1797
1817
|
validateApiConstraints(config, options);
|
|
1798
1818
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
1819
|
+
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
1799
1820
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1800
1821
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
|
|
1801
1822
|
if (config.addons && config.addons.length > 0) {
|
|
@@ -1849,6 +1870,7 @@ const CORE_STACK_FLAGS = new Set([
|
|
|
1849
1870
|
"examples",
|
|
1850
1871
|
"auth",
|
|
1851
1872
|
"dbSetup",
|
|
1873
|
+
"payments",
|
|
1852
1874
|
"api",
|
|
1853
1875
|
"webDeploy",
|
|
1854
1876
|
"serverDeploy"
|
|
@@ -1992,15 +2014,17 @@ const addPackageDependency = async (opts) => {
|
|
|
1992
2014
|
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
1993
2015
|
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
1994
2016
|
for (const pkgName of dependencies) {
|
|
1995
|
-
const version =
|
|
2017
|
+
const version = dependencyVersionMap[pkgName];
|
|
1996
2018
|
if (version) pkgJson.dependencies[pkgName] = version;
|
|
1997
2019
|
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
1998
2020
|
}
|
|
1999
2021
|
for (const pkgName of devDependencies) {
|
|
2000
|
-
const version =
|
|
2022
|
+
const version = dependencyVersionMap[pkgName];
|
|
2001
2023
|
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
2002
2024
|
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
2003
2025
|
}
|
|
2026
|
+
for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
|
|
2027
|
+
for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
|
|
2004
2028
|
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
2005
2029
|
};
|
|
2006
2030
|
|
|
@@ -2062,11 +2086,14 @@ async function setupFumadocs(config) {
|
|
|
2062
2086
|
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
2063
2087
|
const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
|
|
2064
2088
|
const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
2089
|
+
const s = spinner();
|
|
2090
|
+
s.start("Setting up Fumadocs...");
|
|
2065
2091
|
await execa(fumadocsInitCommand, {
|
|
2066
2092
|
cwd: path.join(projectDir, "apps"),
|
|
2067
2093
|
env: { CI: "true" },
|
|
2068
2094
|
shell: true
|
|
2069
2095
|
});
|
|
2096
|
+
s.stop("Fumadocs setup complete!");
|
|
2070
2097
|
const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
|
|
2071
2098
|
const packageJsonPath = path.join(fumadocsDir, "package.json");
|
|
2072
2099
|
if (await fs.pathExists(packageJsonPath)) {
|
|
@@ -2307,11 +2334,14 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2307
2334
|
if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
|
|
2308
2335
|
const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
|
|
2309
2336
|
const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
2337
|
+
const s = spinner();
|
|
2338
|
+
s.start("Setting up Ultracite...");
|
|
2310
2339
|
await execa(ultraciteInitCommand, {
|
|
2311
2340
|
cwd: projectDir,
|
|
2312
2341
|
env: { CI: "true" },
|
|
2313
2342
|
shell: true
|
|
2314
2343
|
});
|
|
2344
|
+
s.stop("Ultracite setup complete!");
|
|
2315
2345
|
if (hasHusky) await addPackageDependency({
|
|
2316
2346
|
devDependencies: ["husky", "lint-staged"],
|
|
2317
2347
|
projectDir
|
|
@@ -2655,8 +2685,12 @@ async function processTemplate(srcPath, destPath, context) {
|
|
|
2655
2685
|
}
|
|
2656
2686
|
handlebars.registerHelper("eq", (a, b) => a === b);
|
|
2657
2687
|
handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
2658
|
-
handlebars.registerHelper("and", (
|
|
2659
|
-
|
|
2688
|
+
handlebars.registerHelper("and", (...args) => {
|
|
2689
|
+
return args.slice(0, -1).every((value) => value);
|
|
2690
|
+
});
|
|
2691
|
+
handlebars.registerHelper("or", (...args) => {
|
|
2692
|
+
return args.slice(0, -1).some((value) => value);
|
|
2693
|
+
});
|
|
2660
2694
|
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
2661
2695
|
|
|
2662
2696
|
//#endregion
|
|
@@ -2718,6 +2752,10 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2718
2752
|
const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
|
|
2719
2753
|
if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
|
|
2720
2754
|
}
|
|
2755
|
+
if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
|
|
2756
|
+
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
|
|
2757
|
+
if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
|
|
2758
|
+
}
|
|
2721
2759
|
}
|
|
2722
2760
|
} else if (hasNuxtWeb) {
|
|
2723
2761
|
const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
|
|
@@ -2758,35 +2796,52 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2758
2796
|
}
|
|
2759
2797
|
}
|
|
2760
2798
|
}
|
|
2761
|
-
async function
|
|
2762
|
-
if (context.
|
|
2799
|
+
async function setupApiPackage(projectDir, context) {
|
|
2800
|
+
if (context.api === "none") return;
|
|
2801
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
2802
|
+
await fs.ensureDir(apiPackageDir);
|
|
2803
|
+
const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
|
|
2804
|
+
if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
|
|
2805
|
+
}
|
|
2806
|
+
async function setupDbPackage(projectDir, context) {
|
|
2807
|
+
if (context.database === "none" || context.orm === "none") return;
|
|
2808
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
2809
|
+
await fs.ensureDir(dbPackageDir);
|
|
2810
|
+
const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
|
|
2811
|
+
if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
|
|
2812
|
+
const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
|
|
2813
|
+
if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
|
|
2814
|
+
}
|
|
2815
|
+
async function setupConvexBackend(projectDir, context) {
|
|
2816
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2817
|
+
if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
|
|
2818
|
+
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2819
|
+
const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
|
|
2820
|
+
await fs.ensureDir(convexBackendDestDir);
|
|
2821
|
+
if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
|
|
2822
|
+
}
|
|
2823
|
+
async function setupServerApp(projectDir, context) {
|
|
2763
2824
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2764
|
-
if (context.backend === "convex") {
|
|
2765
|
-
if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
|
|
2766
|
-
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2767
|
-
const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
|
|
2768
|
-
await fs.ensureDir(convexBackendDestDir);
|
|
2769
|
-
if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
|
|
2770
|
-
return;
|
|
2771
|
-
}
|
|
2772
2825
|
await fs.ensureDir(serverAppDir);
|
|
2773
|
-
const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/
|
|
2826
|
+
const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
|
|
2774
2827
|
if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
|
|
2775
2828
|
const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
|
|
2776
2829
|
if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
|
|
2777
|
-
if (context.api !== "none") {
|
|
2778
|
-
const apiServerBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/base`);
|
|
2779
|
-
if (await fs.pathExists(apiServerBaseDir)) await processAndCopyFiles("**/*", apiServerBaseDir, serverAppDir, context, true);
|
|
2780
|
-
const apiServerFrameworkDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/${context.backend}`);
|
|
2781
|
-
if (await fs.pathExists(apiServerFrameworkDir)) await processAndCopyFiles("**/*", apiServerFrameworkDir, serverAppDir, context, true);
|
|
2782
|
-
}
|
|
2783
2830
|
}
|
|
2784
|
-
async function
|
|
2785
|
-
if (context.backend === "
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2831
|
+
async function setupBackendFramework(projectDir, context) {
|
|
2832
|
+
if (context.backend === "none") return;
|
|
2833
|
+
if (context.backend === "convex") {
|
|
2834
|
+
await setupConvexBackend(projectDir, context);
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
if (context.backend === "self") {
|
|
2838
|
+
await setupApiPackage(projectDir, context);
|
|
2839
|
+
await setupDbPackage(projectDir, context);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
await setupServerApp(projectDir, context);
|
|
2843
|
+
await setupApiPackage(projectDir, context);
|
|
2844
|
+
await setupDbPackage(projectDir, context);
|
|
2790
2845
|
}
|
|
2791
2846
|
async function setupAuthTemplate(projectDir, context) {
|
|
2792
2847
|
if (!context.auth || context.auth === "none") return;
|
|
@@ -2866,21 +2921,21 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2866
2921
|
}
|
|
2867
2922
|
return;
|
|
2868
2923
|
}
|
|
2869
|
-
if (serverAppDirExists && context.backend !== "convex") {
|
|
2924
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
2925
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
2926
|
+
await fs.ensureDir(authPackageDir);
|
|
2870
2927
|
const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
|
|
2871
|
-
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc,
|
|
2872
|
-
if (context.backend === "next") {
|
|
2873
|
-
const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
|
|
2874
|
-
if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
|
|
2875
|
-
}
|
|
2928
|
+
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
|
|
2876
2929
|
if (context.orm !== "none" && context.database !== "none") {
|
|
2930
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
2931
|
+
await fs.ensureDir(dbPackageDir);
|
|
2877
2932
|
const orm = context.orm;
|
|
2878
2933
|
const db = context.database;
|
|
2879
2934
|
let authDbSrc = "";
|
|
2880
2935
|
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
|
|
2881
2936
|
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
|
|
2882
2937
|
else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
|
|
2883
|
-
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc,
|
|
2938
|
+
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
|
|
2884
2939
|
}
|
|
2885
2940
|
}
|
|
2886
2941
|
if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
|
|
@@ -2896,6 +2951,10 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2896
2951
|
if (reactFramework) {
|
|
2897
2952
|
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2898
2953
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2954
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
2955
|
+
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
|
|
2956
|
+
if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
|
|
2957
|
+
}
|
|
2899
2958
|
}
|
|
2900
2959
|
} else if (hasNuxtWeb) {
|
|
2901
2960
|
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
@@ -2926,9 +2985,11 @@ async function setupPaymentsTemplate(projectDir, context) {
|
|
|
2926
2985
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
2927
2986
|
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
2928
2987
|
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
2929
|
-
if (serverAppDirExists && context.backend !== "convex") {
|
|
2988
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
2989
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
2990
|
+
await fs.ensureDir(authPackageDir);
|
|
2930
2991
|
const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
|
|
2931
|
-
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc,
|
|
2992
|
+
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
|
|
2932
2993
|
}
|
|
2933
2994
|
const hasReactWeb = context.frontend.some((f) => [
|
|
2934
2995
|
"tanstack-router",
|
|
@@ -3004,17 +3065,19 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3004
3065
|
for (const example of context.examples) {
|
|
3005
3066
|
if (example === "none") continue;
|
|
3006
3067
|
const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
|
|
3007
|
-
if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
|
|
3068
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
|
|
3008
3069
|
const exampleServerSrc = path.join(exampleBaseDir, "server");
|
|
3009
|
-
if (
|
|
3010
|
-
const
|
|
3011
|
-
|
|
3070
|
+
if (context.api !== "none") {
|
|
3071
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
3072
|
+
await fs.ensureDir(apiPackageDir);
|
|
3073
|
+
const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
|
|
3074
|
+
if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
|
|
3012
3075
|
}
|
|
3013
3076
|
if (context.orm !== "none" && context.database !== "none") {
|
|
3014
|
-
const
|
|
3015
|
-
|
|
3077
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3078
|
+
await fs.ensureDir(dbPackageDir);
|
|
3016
3079
|
const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
|
|
3017
|
-
if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc,
|
|
3080
|
+
if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
|
|
3018
3081
|
}
|
|
3019
3082
|
}
|
|
3020
3083
|
if (webAppDirExists) {
|
|
@@ -3034,6 +3097,10 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3034
3097
|
if (reactFramework) {
|
|
3035
3098
|
const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
|
|
3036
3099
|
if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
|
|
3100
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
3101
|
+
const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
|
|
3102
|
+
if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
|
|
3103
|
+
}
|
|
3037
3104
|
}
|
|
3038
3105
|
}
|
|
3039
3106
|
} else if (hasNuxtWeb) {
|
|
@@ -3081,30 +3148,34 @@ async function handleExtras(projectDir, context) {
|
|
|
3081
3148
|
}
|
|
3082
3149
|
async function setupDockerComposeTemplates(projectDir, context) {
|
|
3083
3150
|
if (context.dbSetup !== "docker" || context.database === "none") return;
|
|
3084
|
-
const
|
|
3151
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3085
3152
|
const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
|
|
3086
|
-
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir,
|
|
3153
|
+
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
|
|
3087
3154
|
}
|
|
3088
3155
|
async function setupDeploymentTemplates(projectDir, context) {
|
|
3089
|
-
|
|
3156
|
+
const isBackendSelf = context.backend === "self";
|
|
3157
|
+
if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
|
|
3090
3158
|
const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
|
|
3091
|
-
if (
|
|
3092
|
-
await
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
await
|
|
3107
|
-
|
|
3159
|
+
if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
|
|
3160
|
+
if (await fs.pathExists(alchemyTemplateSrc)) {
|
|
3161
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
|
|
3162
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3163
|
+
}
|
|
3164
|
+
} else {
|
|
3165
|
+
if (context.webDeploy === "alchemy") {
|
|
3166
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
3167
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
|
|
3168
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
|
|
3169
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
if (context.serverDeploy === "alchemy" && !isBackendSelf) {
|
|
3173
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3174
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
|
|
3175
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3176
|
+
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3177
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3178
|
+
}
|
|
3108
3179
|
}
|
|
3109
3180
|
}
|
|
3110
3181
|
}
|
|
@@ -3127,7 +3198,7 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3127
3198
|
}
|
|
3128
3199
|
}
|
|
3129
3200
|
}
|
|
3130
|
-
if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
|
|
3201
|
+
if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
|
|
3131
3202
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3132
3203
|
if (await fs.pathExists(serverAppDir)) {
|
|
3133
3204
|
const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
|
|
@@ -3135,6 +3206,18 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3135
3206
|
}
|
|
3136
3207
|
}
|
|
3137
3208
|
}
|
|
3209
|
+
async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
|
|
3210
|
+
for (const packageName of [
|
|
3211
|
+
"packages/api",
|
|
3212
|
+
"packages/auth",
|
|
3213
|
+
"packages/db"
|
|
3214
|
+
]) {
|
|
3215
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3216
|
+
if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
|
|
3217
|
+
}
|
|
3218
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3219
|
+
if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3220
|
+
}
|
|
3138
3221
|
|
|
3139
3222
|
//#endregion
|
|
3140
3223
|
//#region src/helpers/core/add-addons.ts
|
|
@@ -3200,7 +3283,7 @@ async function setupServerDeploy(config) {
|
|
|
3200
3283
|
serverDir,
|
|
3201
3284
|
packageManager
|
|
3202
3285
|
});
|
|
3203
|
-
} else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
|
|
3286
|
+
} else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
|
|
3204
3287
|
}
|
|
3205
3288
|
async function setupWorkersServerDeploy(serverDir, _packageManager) {
|
|
3206
3289
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
@@ -3237,18 +3320,18 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
|
|
|
3237
3320
|
log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
|
|
3238
3321
|
}
|
|
3239
3322
|
}
|
|
3240
|
-
async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
3323
|
+
async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
|
|
3241
3324
|
if (!await fs.pathExists(serverDir)) return;
|
|
3242
3325
|
await addPackageDependency({
|
|
3243
3326
|
devDependencies: [
|
|
3244
3327
|
"alchemy",
|
|
3245
3328
|
"wrangler",
|
|
3246
3329
|
"@types/node",
|
|
3247
|
-
"dotenv",
|
|
3248
3330
|
"@cloudflare/workers-types"
|
|
3249
3331
|
],
|
|
3250
3332
|
projectDir: serverDir
|
|
3251
3333
|
});
|
|
3334
|
+
if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
|
|
3252
3335
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
3253
3336
|
if (await fs.pathExists(packageJsonPath)) {
|
|
3254
3337
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
@@ -3261,6 +3344,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
|
3261
3344
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3262
3345
|
}
|
|
3263
3346
|
}
|
|
3347
|
+
async function addAlchemyPackagesDependencies$1(projectDir) {
|
|
3348
|
+
for (const packageName of [
|
|
3349
|
+
"packages/api",
|
|
3350
|
+
"packages/auth",
|
|
3351
|
+
"packages/db"
|
|
3352
|
+
]) {
|
|
3353
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3354
|
+
if (await fs.pathExists(packageDir)) await addPackageDependency({
|
|
3355
|
+
devDependencies: ["@cloudflare/workers-types"],
|
|
3356
|
+
projectDir: packageDir
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3264
3360
|
|
|
3265
3361
|
//#endregion
|
|
3266
3362
|
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
@@ -3271,7 +3367,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3271
3367
|
dependencies: ["@opennextjs/cloudflare"],
|
|
3272
3368
|
devDependencies: [
|
|
3273
3369
|
"alchemy",
|
|
3274
|
-
"dotenv",
|
|
3275
3370
|
"wrangler",
|
|
3276
3371
|
"@cloudflare/workers-types"
|
|
3277
3372
|
],
|
|
@@ -3307,7 +3402,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3307
3402
|
devDependencies: [
|
|
3308
3403
|
"alchemy",
|
|
3309
3404
|
"nitro-cloudflare-dev",
|
|
3310
|
-
"dotenv",
|
|
3311
3405
|
"wrangler"
|
|
3312
3406
|
],
|
|
3313
3407
|
projectDir: webAppDir
|
|
@@ -3370,7 +3464,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
|
|
|
3370
3464
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3371
3465
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3372
3466
|
await addPackageDependency({
|
|
3373
|
-
devDependencies: ["alchemy"
|
|
3467
|
+
devDependencies: ["alchemy"],
|
|
3374
3468
|
projectDir: webAppDir
|
|
3375
3469
|
});
|
|
3376
3470
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3391,7 +3485,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3391
3485
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3392
3486
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3393
3487
|
await addPackageDependency({
|
|
3394
|
-
devDependencies: ["alchemy"
|
|
3488
|
+
devDependencies: ["alchemy"],
|
|
3395
3489
|
projectDir: webAppDir
|
|
3396
3490
|
});
|
|
3397
3491
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3412,11 +3506,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3412
3506
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3413
3507
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3414
3508
|
await addPackageDependency({
|
|
3415
|
-
devDependencies: [
|
|
3416
|
-
"alchemy",
|
|
3417
|
-
"@sveltejs/adapter-cloudflare",
|
|
3418
|
-
"dotenv"
|
|
3419
|
-
],
|
|
3509
|
+
devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
|
|
3420
3510
|
projectDir: webAppDir
|
|
3421
3511
|
});
|
|
3422
3512
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3481,7 +3571,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
|
|
|
3481
3571
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3482
3572
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3483
3573
|
await addPackageDependency({
|
|
3484
|
-
devDependencies: ["alchemy"
|
|
3574
|
+
devDependencies: ["alchemy"],
|
|
3485
3575
|
projectDir: webAppDir
|
|
3486
3576
|
});
|
|
3487
3577
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3502,11 +3592,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3502
3592
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3503
3593
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3504
3594
|
await addPackageDependency({
|
|
3505
|
-
devDependencies: [
|
|
3506
|
-
"alchemy",
|
|
3507
|
-
"dotenv",
|
|
3508
|
-
"@cloudflare/vite-plugin"
|
|
3509
|
-
],
|
|
3595
|
+
devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
|
|
3510
3596
|
projectDir: webAppDir
|
|
3511
3597
|
});
|
|
3512
3598
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3561,7 +3647,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3561
3647
|
//#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
|
|
3562
3648
|
async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
3563
3649
|
await addPackageDependency({
|
|
3564
|
-
devDependencies: ["alchemy"
|
|
3650
|
+
devDependencies: ["alchemy"],
|
|
3565
3651
|
projectDir
|
|
3566
3652
|
});
|
|
3567
3653
|
const rootPkgPath = path.join(projectDir, "package.json");
|
|
@@ -3576,7 +3662,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3576
3662
|
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3577
3663
|
}
|
|
3578
3664
|
const serverDir = path.join(projectDir, "apps/server");
|
|
3579
|
-
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
|
|
3665
|
+
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
|
|
3580
3666
|
const frontend = config.frontend;
|
|
3581
3667
|
const isNext = frontend.includes("next");
|
|
3582
3668
|
const isNuxt = frontend.includes("nuxt");
|
|
@@ -3809,6 +3895,7 @@ async function setupWebDeploy(config) {
|
|
|
3809
3895
|
if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
|
|
3810
3896
|
if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
|
|
3811
3897
|
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
3898
|
+
await addAlchemyPackagesDependencies(projectDir);
|
|
3812
3899
|
return;
|
|
3813
3900
|
}
|
|
3814
3901
|
const isNext = frontend.includes("next");
|
|
@@ -3832,6 +3919,7 @@ async function setupWebDeploy(config) {
|
|
|
3832
3919
|
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
3833
3920
|
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
3834
3921
|
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
3922
|
+
await addAlchemyPackagesDependencies(projectDir);
|
|
3835
3923
|
}
|
|
3836
3924
|
}
|
|
3837
3925
|
async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
@@ -3849,6 +3937,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
|
3849
3937
|
}
|
|
3850
3938
|
await setupWorkersVitePlugin(projectDir);
|
|
3851
3939
|
}
|
|
3940
|
+
async function addAlchemyPackagesDependencies(projectDir) {
|
|
3941
|
+
for (const packageName of [
|
|
3942
|
+
"packages/api",
|
|
3943
|
+
"packages/auth",
|
|
3944
|
+
"packages/db"
|
|
3945
|
+
]) {
|
|
3946
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3947
|
+
if (await fs.pathExists(packageDir)) await addPackageDependency({
|
|
3948
|
+
devDependencies: ["@cloudflare/workers-types"],
|
|
3949
|
+
projectDir: packageDir
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3852
3953
|
|
|
3853
3954
|
//#endregion
|
|
3854
3955
|
//#region src/helpers/core/add-deployment.ts
|
|
@@ -3901,18 +4002,137 @@ async function addDeploymentToProject(input) {
|
|
|
3901
4002
|
}
|
|
3902
4003
|
}
|
|
3903
4004
|
|
|
4005
|
+
//#endregion
|
|
4006
|
+
//#region src/utils/setup-catalogs.ts
|
|
4007
|
+
async function setupCatalogs(projectDir, options) {
|
|
4008
|
+
if (options.packageManager === "npm") return;
|
|
4009
|
+
const packagePaths = [
|
|
4010
|
+
"apps/server",
|
|
4011
|
+
"apps/web",
|
|
4012
|
+
"packages/api",
|
|
4013
|
+
"packages/db",
|
|
4014
|
+
"packages/auth",
|
|
4015
|
+
"packages/backend"
|
|
4016
|
+
];
|
|
4017
|
+
const packagesInfo = [];
|
|
4018
|
+
for (const pkgPath of packagePaths) {
|
|
4019
|
+
const fullPath = path.join(projectDir, pkgPath);
|
|
4020
|
+
const pkgJsonPath = path.join(fullPath, "package.json");
|
|
4021
|
+
if (await fs.pathExists(pkgJsonPath)) {
|
|
4022
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4023
|
+
packagesInfo.push({
|
|
4024
|
+
path: fullPath,
|
|
4025
|
+
dependencies: pkgJson.dependencies || {},
|
|
4026
|
+
devDependencies: pkgJson.devDependencies || {}
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
|
|
4031
|
+
if (Object.keys(catalog).length === 0) return;
|
|
4032
|
+
if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
|
|
4033
|
+
else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
|
|
4034
|
+
await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
|
|
4035
|
+
}
|
|
4036
|
+
function findDuplicateDependencies(packagesInfo, projectName) {
|
|
4037
|
+
const depCount = /* @__PURE__ */ new Map();
|
|
4038
|
+
const projectScope = `@${projectName}/`;
|
|
4039
|
+
for (const pkg of packagesInfo) {
|
|
4040
|
+
const allDeps = {
|
|
4041
|
+
...pkg.dependencies,
|
|
4042
|
+
...pkg.devDependencies
|
|
4043
|
+
};
|
|
4044
|
+
for (const [depName, version] of Object.entries(allDeps)) {
|
|
4045
|
+
if (depName.startsWith(projectScope)) continue;
|
|
4046
|
+
if (version.startsWith("workspace:")) continue;
|
|
4047
|
+
const existing = depCount.get(depName);
|
|
4048
|
+
if (existing) existing.packages.push(pkg.path);
|
|
4049
|
+
else depCount.set(depName, {
|
|
4050
|
+
version,
|
|
4051
|
+
packages: [pkg.path]
|
|
4052
|
+
});
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
const catalog = {};
|
|
4056
|
+
for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
|
|
4057
|
+
return catalog;
|
|
4058
|
+
}
|
|
4059
|
+
async function setupBunCatalogs(projectDir, catalog) {
|
|
4060
|
+
const rootPkgJsonPath = path.join(projectDir, "package.json");
|
|
4061
|
+
const rootPkgJson = await fs.readJson(rootPkgJsonPath);
|
|
4062
|
+
if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
|
|
4063
|
+
if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
|
|
4064
|
+
packages: rootPkgJson.workspaces,
|
|
4065
|
+
catalog
|
|
4066
|
+
};
|
|
4067
|
+
else if (typeof rootPkgJson.workspaces === "object") {
|
|
4068
|
+
if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
|
|
4069
|
+
rootPkgJson.workspaces.catalog = {
|
|
4070
|
+
...rootPkgJson.workspaces.catalog,
|
|
4071
|
+
...catalog
|
|
4072
|
+
};
|
|
4073
|
+
}
|
|
4074
|
+
await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
|
|
4075
|
+
}
|
|
4076
|
+
async function setupPnpmCatalogs(projectDir, catalog) {
|
|
4077
|
+
const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
|
|
4078
|
+
if (!await fs.pathExists(workspaceYamlPath)) return;
|
|
4079
|
+
const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
|
|
4080
|
+
const workspaceYaml = yaml.parse(workspaceContent);
|
|
4081
|
+
if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
|
|
4082
|
+
workspaceYaml.catalog = {
|
|
4083
|
+
...workspaceYaml.catalog,
|
|
4084
|
+
...catalog
|
|
4085
|
+
};
|
|
4086
|
+
await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
|
|
4087
|
+
}
|
|
4088
|
+
async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
|
|
4089
|
+
for (const pkg of packagesInfo) {
|
|
4090
|
+
const pkgJsonPath = path.join(pkg.path, "package.json");
|
|
4091
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4092
|
+
let updated = false;
|
|
4093
|
+
if (pkgJson.dependencies) {
|
|
4094
|
+
for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
|
|
4095
|
+
pkgJson.dependencies[depName] = "catalog:";
|
|
4096
|
+
updated = true;
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
if (pkgJson.devDependencies) {
|
|
4100
|
+
for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
|
|
4101
|
+
pkgJson.devDependencies[depName] = "catalog:";
|
|
4102
|
+
updated = true;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
|
|
3904
4109
|
//#endregion
|
|
3905
4110
|
//#region src/helpers/addons/examples-setup.ts
|
|
3906
4111
|
async function setupExamples(config) {
|
|
3907
|
-
const { examples, frontend, backend, projectDir } = config;
|
|
4112
|
+
const { examples, frontend, backend, projectDir, orm } = config;
|
|
3908
4113
|
if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
|
|
4114
|
+
const apiDir = path.join(projectDir, "packages/api");
|
|
4115
|
+
if (await fs.pathExists(apiDir) && backend !== "none") {
|
|
4116
|
+
if (orm === "drizzle") await addPackageDependency({
|
|
4117
|
+
dependencies: ["drizzle-orm"],
|
|
4118
|
+
projectDir: apiDir
|
|
4119
|
+
});
|
|
4120
|
+
else if (orm === "prisma") await addPackageDependency({
|
|
4121
|
+
dependencies: ["@prisma/client"],
|
|
4122
|
+
projectDir: apiDir
|
|
4123
|
+
});
|
|
4124
|
+
else if (orm === "mongoose") await addPackageDependency({
|
|
4125
|
+
dependencies: ["mongoose"],
|
|
4126
|
+
projectDir: apiDir
|
|
4127
|
+
});
|
|
4128
|
+
}
|
|
3909
4129
|
if (examples.includes("ai")) {
|
|
3910
4130
|
const webClientDir = path.join(projectDir, "apps/web");
|
|
3911
4131
|
const nativeClientDir = path.join(projectDir, "apps/native");
|
|
3912
|
-
const
|
|
4132
|
+
const apiDir$1 = path.join(projectDir, "packages/api");
|
|
3913
4133
|
const webClientDirExists = await fs.pathExists(webClientDir);
|
|
3914
4134
|
const nativeClientDirExists = await fs.pathExists(nativeClientDir);
|
|
3915
|
-
const
|
|
4135
|
+
const apiDirExists = await fs.pathExists(apiDir$1);
|
|
3916
4136
|
const hasNuxt = frontend.includes("nuxt");
|
|
3917
4137
|
const hasSvelte = frontend.includes("svelte");
|
|
3918
4138
|
const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
|
|
@@ -3933,9 +4153,13 @@ async function setupExamples(config) {
|
|
|
3933
4153
|
dependencies: ["ai", "@ai-sdk/react"],
|
|
3934
4154
|
projectDir: nativeClientDir
|
|
3935
4155
|
});
|
|
3936
|
-
if (
|
|
4156
|
+
if (apiDirExists && backend !== "none") await addPackageDependency({
|
|
3937
4157
|
dependencies: ["ai", "@ai-sdk/google"],
|
|
3938
|
-
projectDir:
|
|
4158
|
+
projectDir: apiDir$1
|
|
4159
|
+
});
|
|
4160
|
+
if (backend === "self" && webClientDirExists) await addPackageDependency({
|
|
4161
|
+
dependencies: ["ai", "@ai-sdk/google"],
|
|
4162
|
+
projectDir: webClientDir
|
|
3939
4163
|
});
|
|
3940
4164
|
}
|
|
3941
4165
|
}
|
|
@@ -4058,23 +4282,26 @@ async function setupApi(config) {
|
|
|
4058
4282
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4059
4283
|
const webDirExists = await fs.pathExists(webDir);
|
|
4060
4284
|
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4061
|
-
|
|
4285
|
+
await fs.pathExists(serverDir);
|
|
4062
4286
|
const frontendType = getFrontendType(frontend);
|
|
4063
4287
|
if (!isConvex && api !== "none") {
|
|
4064
4288
|
const apiDeps = getApiDependencies(api, frontendType);
|
|
4065
|
-
|
|
4289
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
4290
|
+
if (apiDeps.server) {
|
|
4066
4291
|
await addPackageDependency({
|
|
4067
4292
|
dependencies: apiDeps.server.dependencies,
|
|
4068
|
-
projectDir:
|
|
4293
|
+
projectDir: apiPackageDir
|
|
4069
4294
|
});
|
|
4070
|
-
if (
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4295
|
+
if (backend === "self" && webDirExists) await addPackageDependency({
|
|
4296
|
+
dependencies: apiDeps.server.dependencies,
|
|
4297
|
+
projectDir: webDir
|
|
4298
|
+
});
|
|
4299
|
+
if (backend === "self") {
|
|
4300
|
+
const frameworkDeps = [];
|
|
4301
|
+
if (frontend.includes("next")) frameworkDeps.push("next");
|
|
4302
|
+
if (frameworkDeps.length > 0) await addPackageDependency({
|
|
4303
|
+
dependencies: frameworkDeps,
|
|
4304
|
+
projectDir: apiPackageDir
|
|
4078
4305
|
});
|
|
4079
4306
|
}
|
|
4080
4307
|
}
|
|
@@ -4120,7 +4347,7 @@ async function setupApi(config) {
|
|
|
4120
4347
|
//#endregion
|
|
4121
4348
|
//#region src/helpers/core/backend-setup.ts
|
|
4122
4349
|
async function setupBackendDependencies(config) {
|
|
4123
|
-
const { backend, runtime, api, projectDir } = config;
|
|
4350
|
+
const { backend, runtime, api, auth, examples, projectDir } = config;
|
|
4124
4351
|
if (backend === "convex") return;
|
|
4125
4352
|
const framework = backend;
|
|
4126
4353
|
const serverDir = path.join(projectDir, "apps/server");
|
|
@@ -4128,27 +4355,23 @@ async function setupBackendDependencies(config) {
|
|
|
4128
4355
|
const devDependencies = [];
|
|
4129
4356
|
if (framework === "hono") {
|
|
4130
4357
|
dependencies.push("hono");
|
|
4131
|
-
if (
|
|
4132
|
-
if (runtime === "node") {
|
|
4133
|
-
dependencies.push("@hono/node-server");
|
|
4134
|
-
devDependencies.push("tsx", "@types/node");
|
|
4135
|
-
}
|
|
4358
|
+
if (runtime === "node") dependencies.push("@hono/node-server");
|
|
4136
4359
|
} else if (framework === "elysia") {
|
|
4137
4360
|
dependencies.push("elysia", "@elysiajs/cors");
|
|
4138
|
-
if (
|
|
4139
|
-
if (runtime === "node") {
|
|
4140
|
-
dependencies.push("@elysiajs/node");
|
|
4141
|
-
devDependencies.push("tsx", "@types/node");
|
|
4142
|
-
}
|
|
4361
|
+
if (runtime === "node") dependencies.push("@elysiajs/node");
|
|
4143
4362
|
} else if (framework === "express") {
|
|
4144
4363
|
dependencies.push("express", "cors");
|
|
4145
4364
|
devDependencies.push("@types/express", "@types/cors");
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
dependencies.push("
|
|
4149
|
-
if (
|
|
4150
|
-
|
|
4151
|
-
if (
|
|
4365
|
+
} else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
|
|
4366
|
+
if (api === "trpc") {
|
|
4367
|
+
dependencies.push("@trpc/server");
|
|
4368
|
+
if (framework === "hono") dependencies.push("@hono/trpc-server");
|
|
4369
|
+
else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
|
|
4370
|
+
} else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
|
|
4371
|
+
if (auth === "better-auth") dependencies.push("better-auth");
|
|
4372
|
+
if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
|
|
4373
|
+
if (runtime === "node") devDependencies.push("tsx", "@types/node");
|
|
4374
|
+
else if (runtime === "bun") devDependencies.push("@types/bun");
|
|
4152
4375
|
if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
|
|
4153
4376
|
dependencies,
|
|
4154
4377
|
devDependencies,
|
|
@@ -4166,7 +4389,7 @@ async function setupAuth(config) {
|
|
|
4166
4389
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
4167
4390
|
const clientDirExists = await fs.pathExists(clientDir);
|
|
4168
4391
|
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4169
|
-
|
|
4392
|
+
await fs.pathExists(serverDir);
|
|
4170
4393
|
try {
|
|
4171
4394
|
if (backend === "convex") {
|
|
4172
4395
|
if (auth === "clerk" && clientDirExists) {
|
|
@@ -4222,9 +4445,11 @@ async function setupAuth(config) {
|
|
|
4222
4445
|
});
|
|
4223
4446
|
return;
|
|
4224
4447
|
}
|
|
4225
|
-
|
|
4448
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
4449
|
+
const authPackageDirExists = await fs.pathExists(authPackageDir);
|
|
4450
|
+
if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
|
|
4226
4451
|
dependencies: ["better-auth"],
|
|
4227
|
-
projectDir:
|
|
4452
|
+
projectDir: authPackageDir
|
|
4228
4453
|
});
|
|
4229
4454
|
if (frontend.some((f) => [
|
|
4230
4455
|
"react-router",
|
|
@@ -4246,9 +4471,9 @@ async function setupAuth(config) {
|
|
|
4246
4471
|
dependencies: ["better-auth", "@better-auth/expo"],
|
|
4247
4472
|
projectDir: nativeDir
|
|
4248
4473
|
});
|
|
4249
|
-
if (
|
|
4474
|
+
if (authPackageDirExists) await addPackageDependency({
|
|
4250
4475
|
dependencies: ["@better-auth/expo"],
|
|
4251
|
-
projectDir:
|
|
4476
|
+
projectDir: authPackageDir
|
|
4252
4477
|
});
|
|
4253
4478
|
}
|
|
4254
4479
|
}
|
|
@@ -4267,6 +4492,34 @@ function generateAuthSecret(length = 32) {
|
|
|
4267
4492
|
|
|
4268
4493
|
//#endregion
|
|
4269
4494
|
//#region src/helpers/core/env-setup.ts
|
|
4495
|
+
function getClientServerVar(frontend, backend) {
|
|
4496
|
+
const hasNextJs = frontend.includes("next");
|
|
4497
|
+
const hasNuxt = frontend.includes("nuxt");
|
|
4498
|
+
const hasSvelte = frontend.includes("svelte");
|
|
4499
|
+
if (backend === "self") return {
|
|
4500
|
+
key: "",
|
|
4501
|
+
value: "",
|
|
4502
|
+
write: false
|
|
4503
|
+
};
|
|
4504
|
+
let key = "VITE_SERVER_URL";
|
|
4505
|
+
if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
|
|
4506
|
+
else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
|
|
4507
|
+
else if (hasSvelte) key = "PUBLIC_SERVER_URL";
|
|
4508
|
+
return {
|
|
4509
|
+
key,
|
|
4510
|
+
value: "http://localhost:3000",
|
|
4511
|
+
write: true
|
|
4512
|
+
};
|
|
4513
|
+
}
|
|
4514
|
+
function getConvexVar(frontend) {
|
|
4515
|
+
const hasNextJs = frontend.includes("next");
|
|
4516
|
+
const hasNuxt = frontend.includes("nuxt");
|
|
4517
|
+
const hasSvelte = frontend.includes("svelte");
|
|
4518
|
+
if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
|
|
4519
|
+
if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
|
|
4520
|
+
if (hasSvelte) return "PUBLIC_CONVEX_URL";
|
|
4521
|
+
return "VITE_CONVEX_URL";
|
|
4522
|
+
}
|
|
4270
4523
|
async function addEnvVariablesToFile(filePath, variables) {
|
|
4271
4524
|
await fs.ensureDir(path.dirname(filePath));
|
|
4272
4525
|
let envContent = "";
|
|
@@ -4325,22 +4578,13 @@ async function setupEnvironmentVariables(config) {
|
|
|
4325
4578
|
if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
|
|
4326
4579
|
const clientDir = path.join(projectDir, "apps/web");
|
|
4327
4580
|
if (await fs.pathExists(clientDir)) {
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
else if (hasNuxt) envVarName = "NUXT_PUBLIC_SERVER_URL";
|
|
4332
|
-
else if (hasSvelte) envVarName = "PUBLIC_SERVER_URL";
|
|
4333
|
-
if (backend === "convex") {
|
|
4334
|
-
if (hasNextJs) envVarName = "NEXT_PUBLIC_CONVEX_URL";
|
|
4335
|
-
else if (hasNuxt) envVarName = "NUXT_PUBLIC_CONVEX_URL";
|
|
4336
|
-
else if (hasSvelte) envVarName = "PUBLIC_CONVEX_URL";
|
|
4337
|
-
else envVarName = "VITE_CONVEX_URL";
|
|
4338
|
-
serverUrl = "https://<YOUR_CONVEX_URL>";
|
|
4339
|
-
}
|
|
4581
|
+
const baseVar = getClientServerVar(frontend, backend);
|
|
4582
|
+
const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
|
|
4583
|
+
const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
|
|
4340
4584
|
const clientVars = [{
|
|
4341
4585
|
key: envVarName,
|
|
4342
4586
|
value: serverUrl,
|
|
4343
|
-
condition: true
|
|
4587
|
+
condition: backend === "convex" ? true : baseVar.write
|
|
4344
4588
|
}];
|
|
4345
4589
|
if (backend === "convex" && auth === "clerk") {
|
|
4346
4590
|
if (hasNextJs) clientVars.push({
|
|
@@ -4388,7 +4632,7 @@ async function setupEnvironmentVariables(config) {
|
|
|
4388
4632
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
4389
4633
|
if (await fs.pathExists(nativeDir)) {
|
|
4390
4634
|
let envVarName = "EXPO_PUBLIC_SERVER_URL";
|
|
4391
|
-
let serverUrl = "http://localhost:3000";
|
|
4635
|
+
let serverUrl = backend === "self" ? "http://localhost:3001" : "http://localhost:3000";
|
|
4392
4636
|
if (backend === "convex") {
|
|
4393
4637
|
envVarName = "EXPO_PUBLIC_CONVEX_URL";
|
|
4394
4638
|
serverUrl = "https://<YOUR_CONVEX_URL>";
|
|
@@ -4431,10 +4675,9 @@ async function setupEnvironmentVariables(config) {
|
|
|
4431
4675
|
return;
|
|
4432
4676
|
}
|
|
4433
4677
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4434
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
4435
|
-
const envPath = path.join(serverDir, ".env");
|
|
4436
4678
|
let corsOrigin = "http://localhost:3001";
|
|
4437
|
-
if (
|
|
4679
|
+
if (backend === "self") corsOrigin = "http://localhost:3001";
|
|
4680
|
+
else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
|
|
4438
4681
|
let databaseUrl = null;
|
|
4439
4682
|
if (database !== "none" && dbSetup === "none") switch (database) {
|
|
4440
4683
|
case "postgres":
|
|
@@ -4448,15 +4691,13 @@ async function setupEnvironmentVariables(config) {
|
|
|
4448
4691
|
break;
|
|
4449
4692
|
case "sqlite":
|
|
4450
4693
|
if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
|
|
4451
|
-
else
|
|
4694
|
+
else {
|
|
4695
|
+
const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
|
|
4696
|
+
databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
|
|
4697
|
+
}
|
|
4452
4698
|
break;
|
|
4453
4699
|
}
|
|
4454
4700
|
const serverVars = [
|
|
4455
|
-
{
|
|
4456
|
-
key: "CORS_ORIGIN",
|
|
4457
|
-
value: corsOrigin,
|
|
4458
|
-
condition: true
|
|
4459
|
-
},
|
|
4460
4701
|
{
|
|
4461
4702
|
key: "BETTER_AUTH_SECRET",
|
|
4462
4703
|
value: generateAuthSecret(),
|
|
@@ -4464,19 +4705,9 @@ async function setupEnvironmentVariables(config) {
|
|
|
4464
4705
|
},
|
|
4465
4706
|
{
|
|
4466
4707
|
key: "BETTER_AUTH_URL",
|
|
4467
|
-
value: "http://localhost:3000",
|
|
4708
|
+
value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
|
|
4468
4709
|
condition: !!auth
|
|
4469
4710
|
},
|
|
4470
|
-
{
|
|
4471
|
-
key: "DATABASE_URL",
|
|
4472
|
-
value: databaseUrl,
|
|
4473
|
-
condition: database !== "none" && dbSetup === "none"
|
|
4474
|
-
},
|
|
4475
|
-
{
|
|
4476
|
-
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4477
|
-
value: "",
|
|
4478
|
-
condition: examples?.includes("ai") || false
|
|
4479
|
-
},
|
|
4480
4711
|
{
|
|
4481
4712
|
key: "POLAR_ACCESS_TOKEN",
|
|
4482
4713
|
value: "",
|
|
@@ -4486,9 +4717,27 @@ async function setupEnvironmentVariables(config) {
|
|
|
4486
4717
|
key: "POLAR_SUCCESS_URL",
|
|
4487
4718
|
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
|
|
4488
4719
|
condition: config.payments === "polar"
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
key: "CORS_ORIGIN",
|
|
4723
|
+
value: corsOrigin,
|
|
4724
|
+
condition: true
|
|
4725
|
+
},
|
|
4726
|
+
{
|
|
4727
|
+
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4728
|
+
value: "",
|
|
4729
|
+
condition: examples?.includes("ai") || false
|
|
4730
|
+
},
|
|
4731
|
+
{
|
|
4732
|
+
key: "DATABASE_URL",
|
|
4733
|
+
value: databaseUrl,
|
|
4734
|
+
condition: database !== "none" && dbSetup === "none"
|
|
4489
4735
|
}
|
|
4490
4736
|
];
|
|
4491
|
-
|
|
4737
|
+
if (backend === "self") {
|
|
4738
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4739
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
|
|
4740
|
+
} else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
|
|
4492
4741
|
const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
|
|
4493
4742
|
const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
|
|
4494
4743
|
if (isUnifiedAlchemy) {
|
|
@@ -4508,12 +4757,15 @@ async function setupEnvironmentVariables(config) {
|
|
|
4508
4757
|
}]);
|
|
4509
4758
|
}
|
|
4510
4759
|
if (serverDeploy === "alchemy") {
|
|
4511
|
-
const
|
|
4512
|
-
if (await fs.pathExists(serverDir$1)) await addEnvVariablesToFile(path.join(serverDir$1, ".env"), [{
|
|
4760
|
+
const serverAlchemyVars = [{
|
|
4513
4761
|
key: "ALCHEMY_PASSWORD",
|
|
4514
4762
|
value: "please-change-this",
|
|
4515
4763
|
condition: true
|
|
4516
|
-
}]
|
|
4764
|
+
}];
|
|
4765
|
+
if (backend === "self") {
|
|
4766
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4767
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
|
|
4768
|
+
} else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
|
|
4517
4769
|
}
|
|
4518
4770
|
}
|
|
4519
4771
|
}
|
|
@@ -4521,9 +4773,10 @@ async function setupEnvironmentVariables(config) {
|
|
|
4521
4773
|
//#endregion
|
|
4522
4774
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4523
4775
|
async function setupCloudflareD1(config) {
|
|
4524
|
-
const { projectDir, serverDeploy, orm } = config;
|
|
4776
|
+
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4525
4777
|
if (serverDeploy === "wrangler") {
|
|
4526
|
-
const
|
|
4778
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
4779
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4527
4780
|
const variables = [
|
|
4528
4781
|
{
|
|
4529
4782
|
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
@@ -4546,16 +4799,17 @@ async function setupCloudflareD1(config) {
|
|
|
4546
4799
|
} catch (_err) {}
|
|
4547
4800
|
}
|
|
4548
4801
|
if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
|
|
4549
|
-
const
|
|
4802
|
+
const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
|
|
4803
|
+
const envPath = path.join(projectDir, targetApp2, ".env");
|
|
4550
4804
|
const variables = [{
|
|
4551
4805
|
key: "DATABASE_URL",
|
|
4552
|
-
value: "
|
|
4806
|
+
value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
|
|
4553
4807
|
condition: true
|
|
4554
4808
|
}];
|
|
4555
4809
|
try {
|
|
4556
4810
|
await addEnvVariablesToFile(envPath, variables);
|
|
4557
4811
|
} catch (_err) {}
|
|
4558
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4812
|
+
const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
|
|
4559
4813
|
await addPackageDependency({
|
|
4560
4814
|
dependencies: ["@prisma/adapter-d1"],
|
|
4561
4815
|
projectDir: serverDir
|
|
@@ -4648,9 +4902,10 @@ async function initMongoDBAtlas(serverDir) {
|
|
|
4648
4902
|
return null;
|
|
4649
4903
|
}
|
|
4650
4904
|
}
|
|
4651
|
-
async function writeEnvFile$3(projectDir, config) {
|
|
4905
|
+
async function writeEnvFile$3(projectDir, backend, config) {
|
|
4652
4906
|
try {
|
|
4653
|
-
const
|
|
4907
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
4908
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4654
4909
|
const variables = [{
|
|
4655
4910
|
key: "DATABASE_URL",
|
|
4656
4911
|
value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
|
|
@@ -4679,14 +4934,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
|
|
|
4679
4934
|
`);
|
|
4680
4935
|
}
|
|
4681
4936
|
async function setupMongoDBAtlas(config, cliInput) {
|
|
4682
|
-
const { projectDir } = config;
|
|
4937
|
+
const { projectDir, backend } = config;
|
|
4683
4938
|
const manualDb = cliInput?.manualDb ?? false;
|
|
4684
|
-
const serverDir = path.join(projectDir, "
|
|
4939
|
+
const serverDir = path.join(projectDir, "packages/db");
|
|
4685
4940
|
try {
|
|
4686
4941
|
await fs.ensureDir(serverDir);
|
|
4687
4942
|
if (manualDb) {
|
|
4688
4943
|
log.info("MongoDB Atlas manual setup selected");
|
|
4689
|
-
await writeEnvFile$3(projectDir);
|
|
4944
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4690
4945
|
displayManualSetupInstructions$3();
|
|
4691
4946
|
return;
|
|
4692
4947
|
}
|
|
@@ -4706,23 +4961,23 @@ async function setupMongoDBAtlas(config, cliInput) {
|
|
|
4706
4961
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
4707
4962
|
if (mode === "manual") {
|
|
4708
4963
|
log.info("MongoDB Atlas manual setup selected");
|
|
4709
|
-
await writeEnvFile$3(projectDir);
|
|
4964
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4710
4965
|
displayManualSetupInstructions$3();
|
|
4711
4966
|
return;
|
|
4712
4967
|
}
|
|
4713
|
-
const
|
|
4714
|
-
if (
|
|
4715
|
-
await writeEnvFile$3(projectDir,
|
|
4968
|
+
const config$1 = await initMongoDBAtlas(serverDir);
|
|
4969
|
+
if (config$1) {
|
|
4970
|
+
await writeEnvFile$3(projectDir, backend, config$1);
|
|
4716
4971
|
log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
|
|
4717
4972
|
} else {
|
|
4718
4973
|
log.warn(pc.yellow("Falling back to local MongoDB configuration"));
|
|
4719
|
-
await writeEnvFile$3(projectDir);
|
|
4974
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4720
4975
|
displayManualSetupInstructions$3();
|
|
4721
4976
|
}
|
|
4722
4977
|
} catch (error) {
|
|
4723
4978
|
consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
4724
4979
|
try {
|
|
4725
|
-
await writeEnvFile$3(projectDir);
|
|
4980
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4726
4981
|
displayManualSetupInstructions$3();
|
|
4727
4982
|
} catch {}
|
|
4728
4983
|
}
|
|
@@ -4779,7 +5034,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
|
|
|
4779
5034
|
}
|
|
4780
5035
|
async function createNeonProject(projectName, regionId, packageManager) {
|
|
4781
5036
|
try {
|
|
4782
|
-
const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
|
|
5037
|
+
const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
|
|
4783
5038
|
const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
|
|
4784
5039
|
const response = JSON.parse(stdout);
|
|
4785
5040
|
if (response.project && response.connection_uris && response.connection_uris.length > 0) {
|
|
@@ -4799,8 +5054,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
|
|
|
4799
5054
|
consola$1.error(pc.red("Failed to create Neon project"));
|
|
4800
5055
|
}
|
|
4801
5056
|
}
|
|
4802
|
-
async function writeEnvFile$2(projectDir, config) {
|
|
4803
|
-
const
|
|
5057
|
+
async function writeEnvFile$2(projectDir, backend, config) {
|
|
5058
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5059
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4804
5060
|
const variables = [{
|
|
4805
5061
|
key: "DATABASE_URL",
|
|
4806
5062
|
value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
|
|
@@ -4809,16 +5065,17 @@ async function writeEnvFile$2(projectDir, config) {
|
|
|
4809
5065
|
await addEnvVariablesToFile(envPath, variables);
|
|
4810
5066
|
return true;
|
|
4811
5067
|
}
|
|
4812
|
-
async function setupWithNeonDb(projectDir, packageManager) {
|
|
5068
|
+
async function setupWithNeonDb(projectDir, packageManager, backend) {
|
|
4813
5069
|
try {
|
|
4814
5070
|
const s = spinner();
|
|
4815
5071
|
s.start("Creating Neon database using neondb...");
|
|
4816
|
-
const
|
|
4817
|
-
|
|
4818
|
-
|
|
5072
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5073
|
+
const targetDir = path.join(projectDir, targetApp);
|
|
5074
|
+
await fs.ensureDir(targetDir);
|
|
5075
|
+
const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
|
|
4819
5076
|
await execa(packageCmd, {
|
|
4820
5077
|
shell: true,
|
|
4821
|
-
cwd:
|
|
5078
|
+
cwd: targetDir
|
|
4822
5079
|
});
|
|
4823
5080
|
s.stop(pc.green("Neon database created successfully!"));
|
|
4824
5081
|
return true;
|
|
@@ -4827,23 +5084,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
|
|
|
4827
5084
|
throw error;
|
|
4828
5085
|
}
|
|
4829
5086
|
}
|
|
4830
|
-
function displayManualSetupInstructions$2() {
|
|
5087
|
+
function displayManualSetupInstructions$2(target) {
|
|
4831
5088
|
log.info(`Manual Neon PostgreSQL Setup Instructions:
|
|
4832
5089
|
|
|
4833
5090
|
1. Visit https://neon.tech and create an account
|
|
4834
5091
|
2. Create a new project from the dashboard
|
|
4835
5092
|
3. Get your connection string
|
|
4836
|
-
4. Add the database URL to the .env file in
|
|
5093
|
+
4. Add the database URL to the .env file in ${target}/.env
|
|
4837
5094
|
|
|
4838
5095
|
DATABASE_URL="your_connection_string"`);
|
|
4839
5096
|
}
|
|
4840
5097
|
async function setupNeonPostgres(config, cliInput) {
|
|
4841
|
-
const { packageManager, projectDir } = config;
|
|
5098
|
+
const { packageManager, projectDir, backend } = config;
|
|
4842
5099
|
const manualDb = cliInput?.manualDb ?? false;
|
|
4843
5100
|
try {
|
|
4844
5101
|
if (manualDb) {
|
|
4845
|
-
await writeEnvFile$2(projectDir);
|
|
4846
|
-
displayManualSetupInstructions$2();
|
|
5102
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5103
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
4847
5104
|
return;
|
|
4848
5105
|
}
|
|
4849
5106
|
const mode = await select({
|
|
@@ -4861,8 +5118,8 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
4861
5118
|
});
|
|
4862
5119
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
4863
5120
|
if (mode === "manual") {
|
|
4864
|
-
await writeEnvFile$2(projectDir);
|
|
4865
|
-
displayManualSetupInstructions$2();
|
|
5121
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5122
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
4866
5123
|
return;
|
|
4867
5124
|
}
|
|
4868
5125
|
const setupMethod = await select({
|
|
@@ -4879,7 +5136,7 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
4879
5136
|
initialValue: "neondb"
|
|
4880
5137
|
});
|
|
4881
5138
|
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
4882
|
-
if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
|
|
5139
|
+
if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
|
|
4883
5140
|
else {
|
|
4884
5141
|
const suggestedProjectName = path.basename(projectDir);
|
|
4885
5142
|
const projectName = await text({
|
|
@@ -4897,22 +5154,22 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
4897
5154
|
if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
|
|
4898
5155
|
const finalSpinner = spinner();
|
|
4899
5156
|
finalSpinner.start("Configuring database connection");
|
|
4900
|
-
await
|
|
4901
|
-
await writeEnvFile$2(projectDir, neonConfig);
|
|
5157
|
+
await writeEnvFile$2(projectDir, backend, neonConfig);
|
|
4902
5158
|
finalSpinner.stop("Neon database configured!");
|
|
4903
5159
|
}
|
|
4904
5160
|
} catch (error) {
|
|
4905
5161
|
if (error instanceof Error) consola$1.error(pc.red(error.message));
|
|
4906
|
-
await writeEnvFile$2(projectDir);
|
|
4907
|
-
displayManualSetupInstructions$2();
|
|
5162
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5163
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
4908
5164
|
}
|
|
4909
5165
|
}
|
|
4910
5166
|
|
|
4911
5167
|
//#endregion
|
|
4912
5168
|
//#region src/helpers/database-providers/planetscale-setup.ts
|
|
4913
5169
|
async function setupPlanetScale(config) {
|
|
4914
|
-
const { projectDir, database, orm } = config;
|
|
4915
|
-
const
|
|
5170
|
+
const { projectDir, database, orm, backend } = config;
|
|
5171
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5172
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4916
5173
|
if (database === "mysql" && orm === "drizzle") {
|
|
4917
5174
|
const variables = [
|
|
4918
5175
|
{
|
|
@@ -4936,7 +5193,7 @@ async function setupPlanetScale(config) {
|
|
|
4936
5193
|
condition: true
|
|
4937
5194
|
}
|
|
4938
5195
|
];
|
|
4939
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5196
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
4940
5197
|
await addEnvVariablesToFile(envPath, variables);
|
|
4941
5198
|
}
|
|
4942
5199
|
if (database === "postgres" && orm === "prisma") {
|
|
@@ -4945,7 +5202,7 @@ async function setupPlanetScale(config) {
|
|
|
4945
5202
|
value: "postgresql://username:password@host/database?sslaccept=strict",
|
|
4946
5203
|
condition: true
|
|
4947
5204
|
}];
|
|
4948
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5205
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
4949
5206
|
await addEnvVariablesToFile(envPath, variables);
|
|
4950
5207
|
}
|
|
4951
5208
|
if (database === "postgres" && orm === "drizzle") {
|
|
@@ -4954,7 +5211,7 @@ async function setupPlanetScale(config) {
|
|
|
4954
5211
|
value: "postgresql://username:password@host/database?sslmode=verify-full",
|
|
4955
5212
|
condition: true
|
|
4956
5213
|
}];
|
|
4957
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5214
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
4958
5215
|
await addEnvVariablesToFile(envPath, variables);
|
|
4959
5216
|
}
|
|
4960
5217
|
if (database === "mysql" && orm === "prisma") {
|
|
@@ -4963,7 +5220,7 @@ async function setupPlanetScale(config) {
|
|
|
4963
5220
|
value: "mysql://username:password@host/database?sslaccept=strict",
|
|
4964
5221
|
condition: true
|
|
4965
5222
|
}];
|
|
4966
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5223
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
4967
5224
|
await addEnvVariablesToFile(envPath, variables);
|
|
4968
5225
|
}
|
|
4969
5226
|
}
|
|
@@ -4998,7 +5255,7 @@ const AVAILABLE_REGIONS = [
|
|
|
4998
5255
|
];
|
|
4999
5256
|
async function setupWithCreateDb(serverDir, packageManager, orm) {
|
|
5000
5257
|
try {
|
|
5001
|
-
log.info("Starting Prisma Postgres setup with create-db.
|
|
5258
|
+
log.info("Starting Prisma Postgres setup with create-db.");
|
|
5002
5259
|
const selectedRegion = await select({
|
|
5003
5260
|
message: "Select your preferred region:",
|
|
5004
5261
|
options: AVAILABLE_REGIONS,
|
|
@@ -5033,7 +5290,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
|
|
|
5033
5290
|
try {
|
|
5034
5291
|
const prismaDir = path.join(serverDir, "prisma");
|
|
5035
5292
|
await fs.ensureDir(prismaDir);
|
|
5036
|
-
log.info("Starting Prisma PostgreSQL setup.
|
|
5293
|
+
log.info("Starting Prisma PostgreSQL setup.");
|
|
5037
5294
|
const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
|
|
5038
5295
|
await execa(prismaInitCommand, {
|
|
5039
5296
|
cwd: serverDir,
|
|
@@ -5055,9 +5312,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
|
|
|
5055
5312
|
return null;
|
|
5056
5313
|
}
|
|
5057
5314
|
}
|
|
5058
|
-
async function writeEnvFile$1(projectDir, config) {
|
|
5315
|
+
async function writeEnvFile$1(projectDir, backend, config) {
|
|
5059
5316
|
try {
|
|
5060
|
-
const
|
|
5317
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5318
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5061
5319
|
const variables = [{
|
|
5062
5320
|
key: "DATABASE_URL",
|
|
5063
5321
|
value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
|
|
@@ -5073,31 +5331,32 @@ async function writeEnvFile$1(projectDir, config) {
|
|
|
5073
5331
|
consola$1.error("Failed to update environment configuration");
|
|
5074
5332
|
}
|
|
5075
5333
|
}
|
|
5076
|
-
async function addDotenvImportToPrismaConfig(projectDir) {
|
|
5334
|
+
async function addDotenvImportToPrismaConfig(projectDir, backend) {
|
|
5077
5335
|
try {
|
|
5078
|
-
const prismaConfigPath = path.join(projectDir, "
|
|
5336
|
+
const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
|
|
5079
5337
|
let content = await fs.readFile(prismaConfigPath, "utf8");
|
|
5080
|
-
content = `import "dotenv
|
|
5338
|
+
content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
|
|
5081
5339
|
await fs.writeFile(prismaConfigPath, content);
|
|
5082
5340
|
} catch (_error) {
|
|
5083
5341
|
consola$1.error("Failed to update prisma.config.ts");
|
|
5084
5342
|
}
|
|
5085
5343
|
}
|
|
5086
|
-
function displayManualSetupInstructions$1() {
|
|
5344
|
+
function displayManualSetupInstructions$1(target) {
|
|
5087
5345
|
log.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
5088
5346
|
|
|
5089
5347
|
1. Visit https://console.prisma.io and create an account
|
|
5090
5348
|
2. Create a new PostgreSQL database from the dashboard
|
|
5091
5349
|
3. Get your database URL
|
|
5092
|
-
4. Add the database URL to the .env file in
|
|
5350
|
+
4. Add the database URL to the .env file in ${target}/.env
|
|
5093
5351
|
|
|
5094
5352
|
DATABASE_URL="your_database_url"`);
|
|
5095
5353
|
}
|
|
5096
|
-
async function addPrismaAccelerateExtension(
|
|
5354
|
+
async function addPrismaAccelerateExtension(projectDir) {
|
|
5097
5355
|
try {
|
|
5356
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5098
5357
|
await addPackageDependency({
|
|
5099
5358
|
dependencies: ["@prisma/extension-accelerate"],
|
|
5100
|
-
projectDir:
|
|
5359
|
+
projectDir: dbPackageDir
|
|
5101
5360
|
});
|
|
5102
5361
|
return true;
|
|
5103
5362
|
} catch (_error) {
|
|
@@ -5106,14 +5365,14 @@ async function addPrismaAccelerateExtension(serverDir) {
|
|
|
5106
5365
|
}
|
|
5107
5366
|
}
|
|
5108
5367
|
async function setupPrismaPostgres(config, cliInput) {
|
|
5109
|
-
const { packageManager, projectDir, orm } = config;
|
|
5368
|
+
const { packageManager, projectDir, orm, backend } = config;
|
|
5110
5369
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5111
|
-
const
|
|
5370
|
+
const dbDir = path.join(projectDir, "packages/db");
|
|
5112
5371
|
try {
|
|
5113
|
-
await fs.ensureDir(
|
|
5372
|
+
await fs.ensureDir(dbDir);
|
|
5114
5373
|
if (manualDb) {
|
|
5115
|
-
await writeEnvFile$1(projectDir);
|
|
5116
|
-
displayManualSetupInstructions$1();
|
|
5374
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5375
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5117
5376
|
return;
|
|
5118
5377
|
}
|
|
5119
5378
|
const mode = await select({
|
|
@@ -5131,8 +5390,8 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5131
5390
|
});
|
|
5132
5391
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5133
5392
|
if (mode === "manual") {
|
|
5134
|
-
await writeEnvFile$1(projectDir);
|
|
5135
|
-
displayManualSetupInstructions$1();
|
|
5393
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5394
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5136
5395
|
return;
|
|
5137
5396
|
}
|
|
5138
5397
|
const setupOptions = [{
|
|
@@ -5152,26 +5411,26 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5152
5411
|
});
|
|
5153
5412
|
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
5154
5413
|
let prismaConfig = null;
|
|
5155
|
-
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(
|
|
5156
|
-
else prismaConfig = await initPrismaDatabase(
|
|
5414
|
+
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
|
|
5415
|
+
else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
|
|
5157
5416
|
if (prismaConfig) {
|
|
5158
|
-
await writeEnvFile$1(projectDir, prismaConfig);
|
|
5417
|
+
await writeEnvFile$1(projectDir, backend, prismaConfig);
|
|
5159
5418
|
if (orm === "prisma") {
|
|
5160
|
-
await addDotenvImportToPrismaConfig(projectDir);
|
|
5161
|
-
await addPrismaAccelerateExtension(
|
|
5419
|
+
await addDotenvImportToPrismaConfig(projectDir, backend);
|
|
5420
|
+
await addPrismaAccelerateExtension(projectDir);
|
|
5162
5421
|
}
|
|
5163
5422
|
const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
|
|
5164
5423
|
log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
|
|
5165
5424
|
if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
|
|
5166
5425
|
} else {
|
|
5167
|
-
await writeEnvFile$1(projectDir);
|
|
5168
|
-
displayManualSetupInstructions$1();
|
|
5426
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5427
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5169
5428
|
}
|
|
5170
5429
|
} catch (error) {
|
|
5171
5430
|
consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5172
5431
|
try {
|
|
5173
|
-
await writeEnvFile$1(projectDir);
|
|
5174
|
-
displayManualSetupInstructions$1();
|
|
5432
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5433
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5175
5434
|
} catch {}
|
|
5176
5435
|
log.info("Setup completed with manual configuration required.");
|
|
5177
5436
|
}
|
|
@@ -5179,9 +5438,10 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5179
5438
|
|
|
5180
5439
|
//#endregion
|
|
5181
5440
|
//#region src/helpers/database-providers/supabase-setup.ts
|
|
5182
|
-
async function writeSupabaseEnvFile(projectDir, databaseUrl) {
|
|
5441
|
+
async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
|
|
5183
5442
|
try {
|
|
5184
|
-
const
|
|
5443
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5444
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5185
5445
|
const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
|
|
5186
5446
|
await addEnvVariablesToFile(envPath, [{
|
|
5187
5447
|
key: "DATABASE_URL",
|
|
@@ -5258,23 +5518,23 @@ function displayManualSupabaseInstructions(output) {
|
|
|
5258
5518
|
log.info(`"Manual Supabase Setup Instructions:"
|
|
5259
5519
|
1. Ensure Docker is installed and running.
|
|
5260
5520
|
2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
|
|
5261
|
-
3. Run \`supabase init\` in your project's \`
|
|
5262
|
-
4. Run \`supabase start\` in your project's \`
|
|
5521
|
+
3. Run \`supabase init\` in your project's \`packages/db\` directory.
|
|
5522
|
+
4. Run \`supabase start\` in your project's \`packages/db\` directory.
|
|
5263
5523
|
5. Copy the 'DB URL' from the output.${output ? `
|
|
5264
5524
|
${pc.bold("Relevant output from `supabase start`:")}
|
|
5265
5525
|
${pc.dim(output)}` : ""}
|
|
5266
|
-
6. Add the DB URL to the .env file in \`
|
|
5526
|
+
6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
|
|
5267
5527
|
${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
|
|
5268
5528
|
}
|
|
5269
5529
|
async function setupSupabase(config, cliInput) {
|
|
5270
|
-
const { projectDir, packageManager } = config;
|
|
5530
|
+
const { projectDir, packageManager, backend } = config;
|
|
5271
5531
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5272
|
-
const serverDir = path.join(projectDir, "
|
|
5532
|
+
const serverDir = path.join(projectDir, "packages", "db");
|
|
5273
5533
|
try {
|
|
5274
5534
|
await fs.ensureDir(serverDir);
|
|
5275
5535
|
if (manualDb) {
|
|
5276
5536
|
displayManualSupabaseInstructions();
|
|
5277
|
-
await writeSupabaseEnvFile(projectDir, "");
|
|
5537
|
+
await writeSupabaseEnvFile(projectDir, backend, "");
|
|
5278
5538
|
return;
|
|
5279
5539
|
}
|
|
5280
5540
|
const mode = await select({
|
|
@@ -5293,7 +5553,7 @@ async function setupSupabase(config, cliInput) {
|
|
|
5293
5553
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5294
5554
|
if (mode === "manual") {
|
|
5295
5555
|
displayManualSupabaseInstructions();
|
|
5296
|
-
await writeSupabaseEnvFile(projectDir, "");
|
|
5556
|
+
await writeSupabaseEnvFile(projectDir, backend, "");
|
|
5297
5557
|
return;
|
|
5298
5558
|
}
|
|
5299
5559
|
if (!await initializeSupabase(serverDir, packageManager)) {
|
|
@@ -5306,7 +5566,7 @@ async function setupSupabase(config, cliInput) {
|
|
|
5306
5566
|
return;
|
|
5307
5567
|
}
|
|
5308
5568
|
const dbUrl = extractDbUrl(supabaseOutput);
|
|
5309
|
-
if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
|
|
5569
|
+
if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
|
|
5310
5570
|
else {
|
|
5311
5571
|
log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
|
|
5312
5572
|
displayManualSupabaseInstructions(supabaseOutput);
|
|
@@ -5434,8 +5694,9 @@ async function createTursoDatabase(dbName, groupName) {
|
|
|
5434
5694
|
s.stop(pc.red("Failed to retrieve database connection details"));
|
|
5435
5695
|
}
|
|
5436
5696
|
}
|
|
5437
|
-
async function writeEnvFile(projectDir, config) {
|
|
5438
|
-
const
|
|
5697
|
+
async function writeEnvFile(projectDir, backend, config) {
|
|
5698
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5699
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5439
5700
|
const variables = [{
|
|
5440
5701
|
key: "DATABASE_URL",
|
|
5441
5702
|
value: config?.dbUrl ?? "",
|
|
@@ -5459,12 +5720,12 @@ DATABASE_URL=your_database_url
|
|
|
5459
5720
|
DATABASE_AUTH_TOKEN=your_auth_token`);
|
|
5460
5721
|
}
|
|
5461
5722
|
async function setupTurso(config, cliInput) {
|
|
5462
|
-
const { orm, projectDir } = config;
|
|
5723
|
+
const { orm, projectDir, backend } = config;
|
|
5463
5724
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5464
5725
|
const setupSpinner = spinner();
|
|
5465
5726
|
try {
|
|
5466
5727
|
if (manualDb) {
|
|
5467
|
-
await writeEnvFile(projectDir);
|
|
5728
|
+
await writeEnvFile(projectDir, backend);
|
|
5468
5729
|
displayManualSetupInstructions();
|
|
5469
5730
|
return;
|
|
5470
5731
|
}
|
|
@@ -5483,7 +5744,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5483
5744
|
});
|
|
5484
5745
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5485
5746
|
if (mode === "manual") {
|
|
5486
|
-
await writeEnvFile(projectDir);
|
|
5747
|
+
await writeEnvFile(projectDir, backend);
|
|
5487
5748
|
displayManualSetupInstructions();
|
|
5488
5749
|
return;
|
|
5489
5750
|
}
|
|
@@ -5493,7 +5754,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5493
5754
|
if (platform === "win32") {
|
|
5494
5755
|
if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
5495
5756
|
log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
|
|
5496
|
-
await writeEnvFile(projectDir);
|
|
5757
|
+
await writeEnvFile(projectDir, backend);
|
|
5497
5758
|
displayManualSetupInstructions();
|
|
5498
5759
|
return;
|
|
5499
5760
|
}
|
|
@@ -5505,7 +5766,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5505
5766
|
});
|
|
5506
5767
|
if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
|
|
5507
5768
|
if (!shouldInstall) {
|
|
5508
|
-
await writeEnvFile(projectDir);
|
|
5769
|
+
await writeEnvFile(projectDir, backend);
|
|
5509
5770
|
displayManualSetupInstructions();
|
|
5510
5771
|
return;
|
|
5511
5772
|
}
|
|
@@ -5527,7 +5788,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5527
5788
|
dbName = dbNameResponse;
|
|
5528
5789
|
try {
|
|
5529
5790
|
const config$1 = await createTursoDatabase(dbName, selectedGroup);
|
|
5530
|
-
await writeEnvFile(projectDir, config$1);
|
|
5791
|
+
await writeEnvFile(projectDir, backend, config$1);
|
|
5531
5792
|
success = true;
|
|
5532
5793
|
} catch (error) {
|
|
5533
5794
|
if (error instanceof Error && error.message === "DATABASE_EXISTS") {
|
|
@@ -5540,7 +5801,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5540
5801
|
} catch (error) {
|
|
5541
5802
|
if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
|
|
5542
5803
|
consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5543
|
-
await writeEnvFile(projectDir);
|
|
5804
|
+
await writeEnvFile(projectDir, backend);
|
|
5544
5805
|
displayManualSetupInstructions();
|
|
5545
5806
|
log.success("Setup completed with manual configuration required.");
|
|
5546
5807
|
}
|
|
@@ -5552,40 +5813,46 @@ async function setupDatabase(config, cliInput) {
|
|
|
5552
5813
|
const { database, orm, dbSetup, backend, projectDir } = config;
|
|
5553
5814
|
if (backend === "convex" || database === "none") {
|
|
5554
5815
|
if (backend !== "convex") {
|
|
5555
|
-
const serverDir
|
|
5556
|
-
const serverDbDir = path.join(serverDir
|
|
5816
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
5817
|
+
const serverDbDir = path.join(serverDir, "src/db");
|
|
5557
5818
|
if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
|
|
5558
5819
|
}
|
|
5559
5820
|
return;
|
|
5560
5821
|
}
|
|
5561
5822
|
const s = spinner();
|
|
5562
|
-
const
|
|
5563
|
-
if (!await fs.pathExists(
|
|
5823
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5824
|
+
if (!await fs.pathExists(dbPackageDir)) return;
|
|
5564
5825
|
try {
|
|
5565
|
-
if (orm === "prisma")
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5826
|
+
if (orm === "prisma") {
|
|
5827
|
+
if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
|
|
5828
|
+
dependencies: [
|
|
5829
|
+
"@prisma/client",
|
|
5830
|
+
"@prisma/adapter-planetscale",
|
|
5831
|
+
"@planetscale/database"
|
|
5832
|
+
],
|
|
5833
|
+
devDependencies: ["prisma"],
|
|
5834
|
+
projectDir: dbPackageDir
|
|
5835
|
+
});
|
|
5836
|
+
else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
|
|
5837
|
+
dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
|
|
5838
|
+
devDependencies: ["prisma"],
|
|
5839
|
+
projectDir: dbPackageDir
|
|
5840
|
+
});
|
|
5841
|
+
else await addPackageDependency({
|
|
5842
|
+
dependencies: ["@prisma/client"],
|
|
5843
|
+
devDependencies: ["prisma"],
|
|
5844
|
+
projectDir: dbPackageDir
|
|
5845
|
+
});
|
|
5846
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
5847
|
+
if (await fs.pathExists(webDir)) await addPackageDependency({
|
|
5848
|
+
dependencies: ["@prisma/client"],
|
|
5849
|
+
projectDir: webDir
|
|
5850
|
+
});
|
|
5851
|
+
} else if (orm === "drizzle") {
|
|
5585
5852
|
if (database === "sqlite") await addPackageDependency({
|
|
5586
5853
|
dependencies: ["drizzle-orm", "@libsql/client"],
|
|
5587
5854
|
devDependencies: ["drizzle-kit"],
|
|
5588
|
-
projectDir:
|
|
5855
|
+
projectDir: dbPackageDir
|
|
5589
5856
|
});
|
|
5590
5857
|
else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
|
|
5591
5858
|
dependencies: [
|
|
@@ -5594,32 +5861,32 @@ async function setupDatabase(config, cliInput) {
|
|
|
5594
5861
|
"ws"
|
|
5595
5862
|
],
|
|
5596
5863
|
devDependencies: ["drizzle-kit", "@types/ws"],
|
|
5597
|
-
projectDir:
|
|
5864
|
+
projectDir: dbPackageDir
|
|
5598
5865
|
});
|
|
5599
5866
|
else if (dbSetup === "planetscale") await addPackageDependency({
|
|
5600
5867
|
dependencies: ["drizzle-orm", "pg"],
|
|
5601
5868
|
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5602
|
-
projectDir:
|
|
5869
|
+
projectDir: dbPackageDir
|
|
5603
5870
|
});
|
|
5604
5871
|
else await addPackageDependency({
|
|
5605
5872
|
dependencies: ["drizzle-orm", "pg"],
|
|
5606
5873
|
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5607
|
-
projectDir:
|
|
5874
|
+
projectDir: dbPackageDir
|
|
5608
5875
|
});
|
|
5609
5876
|
else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
|
|
5610
5877
|
dependencies: ["drizzle-orm", "@planetscale/database"],
|
|
5611
5878
|
devDependencies: ["drizzle-kit"],
|
|
5612
|
-
projectDir:
|
|
5879
|
+
projectDir: dbPackageDir
|
|
5613
5880
|
});
|
|
5614
5881
|
else await addPackageDependency({
|
|
5615
5882
|
dependencies: ["drizzle-orm", "mysql2"],
|
|
5616
5883
|
devDependencies: ["drizzle-kit"],
|
|
5617
|
-
projectDir:
|
|
5884
|
+
projectDir: dbPackageDir
|
|
5618
5885
|
});
|
|
5619
5886
|
} else if (orm === "mongoose") await addPackageDependency({
|
|
5620
5887
|
dependencies: ["mongoose"],
|
|
5621
5888
|
devDependencies: [],
|
|
5622
|
-
projectDir:
|
|
5889
|
+
projectDir: dbPackageDir
|
|
5623
5890
|
});
|
|
5624
5891
|
if (dbSetup === "docker") await setupDockerCompose(config);
|
|
5625
5892
|
else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
|
|
@@ -5642,7 +5909,7 @@ async function setupDatabase(config, cliInput) {
|
|
|
5642
5909
|
//#region src/helpers/core/runtime-setup.ts
|
|
5643
5910
|
async function setupRuntime(config) {
|
|
5644
5911
|
const { runtime, backend, projectDir } = config;
|
|
5645
|
-
if (backend === "convex" || backend === "
|
|
5912
|
+
if (backend === "convex" || backend === "self" || runtime === "none") return;
|
|
5646
5913
|
const serverDir = path.join(projectDir, "apps/server");
|
|
5647
5914
|
if (!await fs.pathExists(serverDir)) return;
|
|
5648
5915
|
if (runtime === "bun") await setupBunRuntime(serverDir, backend);
|
|
@@ -5732,7 +5999,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
|
|
|
5732
5999
|
${packageManagerRunCmd} dev:setup
|
|
5733
6000
|
\`\`\`
|
|
5734
6001
|
|
|
5735
|
-
Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
|
|
6002
|
+
Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy, options.backend)}
|
|
5736
6003
|
|
|
5737
6004
|
Then, run the development server:
|
|
5738
6005
|
|
|
@@ -5785,6 +6052,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
|
|
|
5785
6052
|
const instructions = [];
|
|
5786
6053
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
5787
6054
|
const isBackendNone = backend === "none";
|
|
6055
|
+
const isBackendSelf = backend === "self";
|
|
5788
6056
|
if (!hasFrontendNone) {
|
|
5789
6057
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
5790
6058
|
const hasReactRouter = frontend.includes("react-router");
|
|
@@ -5793,17 +6061,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
|
|
|
5793
6061
|
const hasSvelte = frontend.includes("svelte");
|
|
5794
6062
|
const hasNuxt = frontend.includes("nuxt");
|
|
5795
6063
|
const hasSolid = frontend.includes("solid");
|
|
5796
|
-
if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see
|
|
6064
|
+
if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) if (isBackendSelf) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see your fullstack application.`);
|
|
6065
|
+
else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
|
|
5797
6066
|
}
|
|
5798
6067
|
if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
|
|
5799
6068
|
if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
|
|
5800
|
-
else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
6069
|
+
else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
5801
6070
|
return instructions.join("\n");
|
|
5802
6071
|
}
|
|
5803
6072
|
function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
|
|
5804
6073
|
const structure = [`${projectName}/`, "├── apps/"];
|
|
5805
6074
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
5806
6075
|
const isBackendNone = backend === "none";
|
|
6076
|
+
const isBackendSelf = backend === "self";
|
|
5807
6077
|
if (!hasFrontendNone) {
|
|
5808
6078
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
5809
6079
|
const hasReactRouter = frontend.includes("react-router");
|
|
@@ -5821,21 +6091,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
|
|
|
5821
6091
|
else if (hasSvelte) frontendType = "SvelteKit";
|
|
5822
6092
|
else if (hasNuxt) frontendType = "Nuxt";
|
|
5823
6093
|
else if (hasSolid) frontendType = "SolidJS";
|
|
5824
|
-
structure.push(`│
|
|
6094
|
+
if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
|
|
6095
|
+
else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
|
|
5825
6096
|
}
|
|
5826
6097
|
}
|
|
5827
|
-
if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
5828
|
-
|
|
5829
|
-
if (
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
|
|
5833
|
-
} else if (!isBackendNone) {
|
|
6098
|
+
if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
6099
|
+
else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
6100
|
+
if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
|
|
6101
|
+
else structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
|
|
6102
|
+
if (!isBackendSelf && !isBackendNone && !isConvex) {
|
|
5834
6103
|
const backendName = backend[0].toUpperCase() + backend.slice(1);
|
|
5835
6104
|
const apiName = api !== "none" ? api.toUpperCase() : "";
|
|
5836
6105
|
const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
|
|
5837
6106
|
structure.push(`│ └── server/ # Backend API (${backendDesc})`);
|
|
5838
6107
|
}
|
|
6108
|
+
if (isConvex || !isBackendNone) {
|
|
6109
|
+
structure.push("├── packages/");
|
|
6110
|
+
if (isConvex) {
|
|
6111
|
+
structure.push("│ ├── backend/ # Convex backend functions and schema");
|
|
6112
|
+
if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
|
|
6113
|
+
}
|
|
6114
|
+
if (!isConvex) {
|
|
6115
|
+
structure.push("│ ├── api/ # API layer / business logic");
|
|
6116
|
+
if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
|
|
6117
|
+
if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
5839
6120
|
return structure.join("\n");
|
|
5840
6121
|
}
|
|
5841
6122
|
function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
|
|
@@ -5871,7 +6152,6 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5871
6152
|
else if (backend === "express") addonsList.push("- **Express** - Fast, unopinionated web framework");
|
|
5872
6153
|
else if (backend === "fastify") addonsList.push("- **Fastify** - Fast, low-overhead web framework");
|
|
5873
6154
|
else if (backend === "elysia") addonsList.push("- **Elysia** - Type-safe, high-performance framework");
|
|
5874
|
-
else if (backend === "next") addonsList.push("- **Next.js** - Full-stack React framework");
|
|
5875
6155
|
if (api === "trpc") addonsList.push("- **tRPC** - End-to-end type-safe APIs");
|
|
5876
6156
|
else if (api === "orpc") addonsList.push("- **oRPC** - End-to-end type-safe APIs with OpenAPI integration");
|
|
5877
6157
|
if (runtime !== "none") addonsList.push(`- **${runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime}** - Runtime environment`);
|
|
@@ -5893,33 +6173,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5893
6173
|
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
5894
6174
|
return addonsList.join("\n");
|
|
5895
6175
|
}
|
|
5896
|
-
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
|
|
6176
|
+
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
|
|
5897
6177
|
if (database === "none") return "";
|
|
6178
|
+
const isBackendSelf = backend === "self";
|
|
6179
|
+
const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
|
|
6180
|
+
const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
|
|
5898
6181
|
let setup = "## Database Setup\n\n";
|
|
5899
6182
|
if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
5900
6183
|
|
|
5901
6184
|
1. Start the local SQLite database:
|
|
5902
6185
|
${dbSetup === "d1" ? serverDeploy === "alchemy" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
|
|
5903
|
-
cd
|
|
6186
|
+
cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
|
|
5904
6187
|
\`\`\`
|
|
5905
6188
|
`}
|
|
5906
6189
|
|
|
5907
|
-
2. Update your \`.env\` file in the
|
|
6190
|
+
2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
|
|
5908
6191
|
`;
|
|
5909
6192
|
else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
5910
6193
|
|
|
5911
6194
|
1. Make sure you have a PostgreSQL database set up.
|
|
5912
|
-
2. Update your \`
|
|
6195
|
+
2. Update your \`${envPath}\` file with your PostgreSQL connection details.
|
|
5913
6196
|
`;
|
|
5914
6197
|
else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
5915
6198
|
|
|
5916
6199
|
1. Make sure you have a MySQL database set up.
|
|
5917
|
-
2. Update your \`
|
|
6200
|
+
2. Update your \`${envPath}\` file with your MySQL connection details.
|
|
5918
6201
|
`;
|
|
5919
6202
|
else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
|
|
5920
6203
|
|
|
5921
6204
|
1. Make sure you have MongoDB set up.
|
|
5922
|
-
2. Update your \`
|
|
6205
|
+
2. Update your \`${envPath}\` file with your MongoDB connection URI.
|
|
5923
6206
|
`;
|
|
5924
6207
|
setup += `
|
|
5925
6208
|
3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
|
|
@@ -5938,13 +6221,14 @@ ${packageManagerRunCmd} db:push
|
|
|
5938
6221
|
function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
|
|
5939
6222
|
const isConvex = backend === "convex";
|
|
5940
6223
|
const isBackendNone = backend === "none";
|
|
6224
|
+
const isBackendSelf = backend === "self";
|
|
5941
6225
|
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
|
|
5942
6226
|
- \`${packageManagerRunCmd} build\`: Build all applications`;
|
|
5943
|
-
scripts += `
|
|
6227
|
+
if (!isBackendSelf) scripts += `
|
|
5944
6228
|
- \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
|
|
5945
6229
|
if (isConvex) scripts += `
|
|
5946
6230
|
- \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
|
|
5947
|
-
else if (!isBackendNone) scripts += `
|
|
6231
|
+
else if (!isBackendNone && !isBackendSelf) scripts += `
|
|
5948
6232
|
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
|
|
5949
6233
|
scripts += `
|
|
5950
6234
|
- \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
|
|
@@ -5955,7 +6239,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
|
|
|
5955
6239
|
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
|
|
5956
6240
|
- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
|
|
5957
6241
|
if (database === "sqlite" && orm === "drizzle") scripts += `
|
|
5958
|
-
- \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
|
|
6242
|
+
- \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
|
|
5959
6243
|
}
|
|
5960
6244
|
if (addons.includes("biome")) scripts += `
|
|
5961
6245
|
- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
|
|
@@ -6031,15 +6315,14 @@ async function initializeGit(projectDir, useGit) {
|
|
|
6031
6315
|
async function setupPayments(config) {
|
|
6032
6316
|
const { payments, projectDir, frontend } = config;
|
|
6033
6317
|
if (!payments || payments === "none") return;
|
|
6034
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
6035
6318
|
const clientDir = path.join(projectDir, "apps/web");
|
|
6036
|
-
const
|
|
6319
|
+
const authDir = path.join(projectDir, "packages/auth");
|
|
6037
6320
|
const clientDirExists = await fs.pathExists(clientDir);
|
|
6038
|
-
|
|
6321
|
+
const authDirExists = await fs.pathExists(authDir);
|
|
6039
6322
|
if (payments === "polar") {
|
|
6040
|
-
await addPackageDependency({
|
|
6323
|
+
if (authDirExists) await addPackageDependency({
|
|
6041
6324
|
dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
|
|
6042
|
-
projectDir:
|
|
6325
|
+
projectDir: authDir
|
|
6043
6326
|
});
|
|
6044
6327
|
if (clientDirExists) {
|
|
6045
6328
|
if (frontend.some((f) => [
|
|
@@ -6113,19 +6396,20 @@ async function getDockerStatus(database) {
|
|
|
6113
6396
|
async function displayPostInstallInstructions(config) {
|
|
6114
6397
|
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
6115
6398
|
const isConvex = backend === "convex";
|
|
6399
|
+
const isBackendSelf = backend === "self";
|
|
6116
6400
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
6117
6401
|
const cdCmd = `cd ${relativePath}`;
|
|
6118
6402
|
const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
|
|
6119
|
-
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
|
|
6403
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6120
6404
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6121
6405
|
const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
|
|
6122
|
-
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
|
|
6406
|
+
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf) : "";
|
|
6123
6407
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6124
6408
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6125
6409
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
6126
|
-
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
|
|
6127
|
-
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6128
|
-
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6410
|
+
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
6411
|
+
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6412
|
+
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6129
6413
|
const hasWeb = frontend?.some((f) => [
|
|
6130
6414
|
"tanstack-router",
|
|
6131
6415
|
"react-router",
|
|
@@ -6149,7 +6433,8 @@ async function displayPostInstallInstructions(config) {
|
|
|
6149
6433
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
6150
6434
|
output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
|
|
6151
6435
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
6152
|
-
} else {
|
|
6436
|
+
} else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6437
|
+
else {
|
|
6153
6438
|
if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6154
6439
|
if (runtime === "workers") {
|
|
6155
6440
|
if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
|
|
@@ -6160,11 +6445,11 @@ async function displayPostInstallInstructions(config) {
|
|
|
6160
6445
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
6161
6446
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
6162
6447
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
6163
|
-
if (!isConvex) {
|
|
6448
|
+
if (!isConvex && !isBackendSelf) {
|
|
6164
6449
|
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6165
|
-
if (api === "orpc")
|
|
6166
|
-
else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6450
|
+
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6167
6451
|
}
|
|
6452
|
+
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
|
|
6168
6453
|
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
6169
6454
|
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
6170
6455
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
@@ -6184,9 +6469,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
6184
6469
|
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
|
|
6185
6470
|
consola$1.box(output);
|
|
6186
6471
|
}
|
|
6187
|
-
function getNativeInstructions(isConvex) {
|
|
6472
|
+
function getNativeInstructions(isConvex, isBackendSelf) {
|
|
6188
6473
|
const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
|
|
6189
|
-
const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : "http://<YOUR_LOCAL_IP>:3000";
|
|
6474
|
+
const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
|
|
6190
6475
|
const envFileName = ".env";
|
|
6191
6476
|
const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
|
|
6192
6477
|
let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
|
|
@@ -6196,7 +6481,7 @@ function getNativeInstructions(isConvex) {
|
|
|
6196
6481
|
function getLintingInstructions(runCmd) {
|
|
6197
6482
|
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
|
|
6198
6483
|
}
|
|
6199
|
-
async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
|
|
6484
|
+
async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
|
|
6200
6485
|
const instructions = [];
|
|
6201
6486
|
if (dbSetup === "docker") {
|
|
6202
6487
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -6210,8 +6495,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
|
|
|
6210
6495
|
const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
|
|
6211
6496
|
instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
|
|
6212
6497
|
instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
|
|
6213
|
-
|
|
6214
|
-
instructions.push(`${pc.cyan("
|
|
6498
|
+
const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
|
|
6499
|
+
instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
|
|
6500
|
+
instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
|
|
6215
6501
|
instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
|
|
6216
6502
|
instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
|
|
6217
6503
|
}
|
|
@@ -6235,7 +6521,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
|
|
|
6235
6521
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6236
6522
|
if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6237
6523
|
if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6238
|
-
if (database === "sqlite" && dbSetup !== "d1")
|
|
6524
|
+
if (database === "sqlite" && dbSetup !== "d1") {
|
|
6525
|
+
const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
|
|
6526
|
+
instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
|
|
6527
|
+
}
|
|
6239
6528
|
} else if (orm === "mongoose") {
|
|
6240
6529
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6241
6530
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
@@ -6256,32 +6545,124 @@ function getNoOrmWarning() {
|
|
|
6256
6545
|
function getBunWebNativeWarning() {
|
|
6257
6546
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
6258
6547
|
}
|
|
6259
|
-
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
6548
|
+
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6260
6549
|
const instructions = [];
|
|
6261
|
-
if (webDeploy === "wrangler")
|
|
6262
|
-
|
|
6550
|
+
if (webDeploy === "wrangler") {
|
|
6551
|
+
const deployPath = backend === "self" ? "apps/web" : "apps/web";
|
|
6552
|
+
instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
|
|
6553
|
+
}
|
|
6554
|
+
if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
|
|
6263
6555
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6264
6556
|
}
|
|
6265
6557
|
function getClerkInstructions() {
|
|
6266
6558
|
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
6267
6559
|
}
|
|
6268
|
-
function getPolarInstructions() {
|
|
6269
|
-
|
|
6560
|
+
function getPolarInstructions(backend) {
|
|
6561
|
+
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
6562
|
+
return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
|
|
6270
6563
|
}
|
|
6271
|
-
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
6564
|
+
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6272
6565
|
const instructions = [];
|
|
6566
|
+
const isBackendSelf = backend === "self";
|
|
6273
6567
|
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
|
|
6274
|
-
else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
|
|
6275
|
-
else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
|
|
6568
|
+
else if (serverDeploy === "alchemy" && webDeploy !== "alchemy" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
|
|
6569
|
+
else if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
|
|
6276
6570
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6277
6571
|
}
|
|
6278
6572
|
|
|
6573
|
+
//#endregion
|
|
6574
|
+
//#region src/helpers/core/workspace-setup.ts
|
|
6575
|
+
async function setupWorkspaceDependencies(projectDir, options) {
|
|
6576
|
+
const projectName = options.projectName;
|
|
6577
|
+
const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
|
|
6578
|
+
const commonDeps = ["dotenv", "zod"];
|
|
6579
|
+
const commonDevDeps = ["tsdown"];
|
|
6580
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
6581
|
+
if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
|
|
6582
|
+
dependencies: commonDeps,
|
|
6583
|
+
devDependencies: commonDevDeps,
|
|
6584
|
+
projectDir: dbPackageDir
|
|
6585
|
+
});
|
|
6586
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
6587
|
+
if (await fs.pathExists(authPackageDir)) await addPackageDependency({
|
|
6588
|
+
dependencies: commonDeps,
|
|
6589
|
+
devDependencies: commonDevDeps,
|
|
6590
|
+
customDependencies: { [`@${projectName}/db`]: workspaceVersion },
|
|
6591
|
+
projectDir: authPackageDir
|
|
6592
|
+
});
|
|
6593
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
6594
|
+
if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
|
|
6595
|
+
dependencies: commonDeps,
|
|
6596
|
+
devDependencies: commonDevDeps,
|
|
6597
|
+
customDependencies: {
|
|
6598
|
+
[`@${projectName}/auth`]: workspaceVersion,
|
|
6599
|
+
[`@${projectName}/db`]: workspaceVersion
|
|
6600
|
+
},
|
|
6601
|
+
projectDir: apiPackageDir
|
|
6602
|
+
});
|
|
6603
|
+
const serverPackageDir = path.join(projectDir, "apps/server");
|
|
6604
|
+
if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
|
|
6605
|
+
dependencies: commonDeps,
|
|
6606
|
+
devDependencies: commonDevDeps,
|
|
6607
|
+
customDependencies: {
|
|
6608
|
+
[`@${projectName}/api`]: workspaceVersion,
|
|
6609
|
+
[`@${projectName}/auth`]: workspaceVersion,
|
|
6610
|
+
[`@${projectName}/db`]: workspaceVersion
|
|
6611
|
+
},
|
|
6612
|
+
projectDir: serverPackageDir
|
|
6613
|
+
});
|
|
6614
|
+
const webPackageDir = path.join(projectDir, "apps/web");
|
|
6615
|
+
if (await fs.pathExists(webPackageDir)) {
|
|
6616
|
+
const webDeps = {};
|
|
6617
|
+
webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6618
|
+
webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6619
|
+
webDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6620
|
+
if (Object.keys(webDeps).length > 0) await addPackageDependency({
|
|
6621
|
+
customDependencies: webDeps,
|
|
6622
|
+
projectDir: webPackageDir
|
|
6623
|
+
});
|
|
6624
|
+
}
|
|
6625
|
+
const nativePackageDir = path.join(projectDir, "apps/native");
|
|
6626
|
+
if (await fs.pathExists(nativePackageDir)) {
|
|
6627
|
+
const nativeDeps = {};
|
|
6628
|
+
nativeDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6629
|
+
if (Object.keys(nativeDeps).length > 0) await addPackageDependency({
|
|
6630
|
+
customDependencies: nativeDeps,
|
|
6631
|
+
projectDir: nativePackageDir
|
|
6632
|
+
});
|
|
6633
|
+
}
|
|
6634
|
+
const runtimeDevDeps = getRuntimeDevDeps(options);
|
|
6635
|
+
await addPackageDependency({
|
|
6636
|
+
dependencies: commonDeps,
|
|
6637
|
+
devDependencies: [...commonDevDeps, ...runtimeDevDeps],
|
|
6638
|
+
projectDir
|
|
6639
|
+
});
|
|
6640
|
+
}
|
|
6641
|
+
function getRuntimeDevDeps(options) {
|
|
6642
|
+
const { runtime, backend } = options;
|
|
6643
|
+
if (runtime === "none" && backend === "self") return ["@types/node"];
|
|
6644
|
+
if (runtime === "node") return ["@types/node"];
|
|
6645
|
+
if (runtime === "bun") return ["@types/bun"];
|
|
6646
|
+
if (runtime === "workers") return ["@types/node"];
|
|
6647
|
+
return [];
|
|
6648
|
+
}
|
|
6649
|
+
|
|
6279
6650
|
//#endregion
|
|
6280
6651
|
//#region src/helpers/core/project-config.ts
|
|
6281
6652
|
async function updatePackageConfigurations(projectDir, options) {
|
|
6282
6653
|
await updateRootPackageJson(projectDir, options);
|
|
6283
|
-
if (options.backend
|
|
6284
|
-
else
|
|
6654
|
+
if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
|
|
6655
|
+
else if (options.backend === "self") {
|
|
6656
|
+
await updateDbPackageJson(projectDir, options);
|
|
6657
|
+
await updateAuthPackageJson(projectDir, options);
|
|
6658
|
+
await updateApiPackageJson(projectDir, options);
|
|
6659
|
+
await setupWorkspaceDependencies(projectDir, options);
|
|
6660
|
+
} else if (options.backend !== "none") {
|
|
6661
|
+
await updateServerPackageJson(projectDir, options);
|
|
6662
|
+
await updateAuthPackageJson(projectDir, options);
|
|
6663
|
+
await updateApiPackageJson(projectDir, options);
|
|
6664
|
+
await setupWorkspaceDependencies(projectDir, options);
|
|
6665
|
+
}
|
|
6285
6666
|
}
|
|
6286
6667
|
async function updateRootPackageJson(projectDir, options) {
|
|
6287
6668
|
const rootPackageJsonPath = path.join(projectDir, "package.json");
|
|
@@ -6291,6 +6672,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6291
6672
|
if (!packageJson.scripts) packageJson.scripts = {};
|
|
6292
6673
|
const scripts = packageJson.scripts;
|
|
6293
6674
|
const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
|
|
6675
|
+
const dbPackageName = `@${options.projectName}/db`;
|
|
6294
6676
|
let serverDevScript = "";
|
|
6295
6677
|
if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
|
|
6296
6678
|
else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
|
|
@@ -6307,24 +6689,24 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6307
6689
|
scripts["check-types"] = "turbo check-types";
|
|
6308
6690
|
scripts["dev:native"] = "turbo -F native dev";
|
|
6309
6691
|
scripts["dev:web"] = "turbo -F web dev";
|
|
6310
|
-
scripts["dev:server"] = serverDevScript;
|
|
6692
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6311
6693
|
if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
|
|
6312
6694
|
if (needsDbScripts) {
|
|
6313
|
-
scripts["db:push"] = `turbo -F ${
|
|
6314
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${
|
|
6695
|
+
scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
|
|
6696
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
|
|
6315
6697
|
if (options.orm === "prisma") {
|
|
6316
|
-
scripts["db:generate"] = `turbo -F ${
|
|
6317
|
-
scripts["db:migrate"] = `turbo -F ${
|
|
6698
|
+
scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
|
|
6699
|
+
scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
|
|
6318
6700
|
} else if (options.orm === "drizzle") {
|
|
6319
|
-
scripts["db:generate"] = `turbo -F ${
|
|
6320
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${
|
|
6701
|
+
scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
|
|
6702
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
|
|
6321
6703
|
}
|
|
6322
6704
|
}
|
|
6323
6705
|
if (options.dbSetup === "docker") {
|
|
6324
|
-
scripts["db:start"] = `turbo -F ${
|
|
6325
|
-
scripts["db:watch"] = `turbo -F ${
|
|
6326
|
-
scripts["db:stop"] = `turbo -F ${
|
|
6327
|
-
scripts["db:down"] = `turbo -F ${
|
|
6706
|
+
scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
|
|
6707
|
+
scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
|
|
6708
|
+
scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
|
|
6709
|
+
scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
|
|
6328
6710
|
}
|
|
6329
6711
|
} else if (options.packageManager === "pnpm") {
|
|
6330
6712
|
scripts.dev = devScript;
|
|
@@ -6332,24 +6714,24 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6332
6714
|
scripts["check-types"] = "pnpm -r check-types";
|
|
6333
6715
|
scripts["dev:native"] = "pnpm --filter native dev";
|
|
6334
6716
|
scripts["dev:web"] = "pnpm --filter web dev";
|
|
6335
|
-
scripts["dev:server"] = serverDevScript;
|
|
6717
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6336
6718
|
if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
|
|
6337
6719
|
if (needsDbScripts) {
|
|
6338
|
-
scripts["db:push"] = `pnpm --filter ${
|
|
6339
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${
|
|
6720
|
+
scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
|
|
6721
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
|
|
6340
6722
|
if (options.orm === "prisma") {
|
|
6341
|
-
scripts["db:generate"] = `pnpm --filter ${
|
|
6342
|
-
scripts["db:migrate"] = `pnpm --filter ${
|
|
6723
|
+
scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
|
|
6724
|
+
scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
|
|
6343
6725
|
} else if (options.orm === "drizzle") {
|
|
6344
|
-
scripts["db:generate"] = `pnpm --filter ${
|
|
6345
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${
|
|
6726
|
+
scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
|
|
6727
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
|
|
6346
6728
|
}
|
|
6347
6729
|
}
|
|
6348
6730
|
if (options.dbSetup === "docker") {
|
|
6349
|
-
scripts["db:start"] = `pnpm --filter ${
|
|
6350
|
-
scripts["db:watch"] = `pnpm --filter ${
|
|
6351
|
-
scripts["db:stop"] = `pnpm --filter ${
|
|
6352
|
-
scripts["db:down"] = `pnpm --filter ${
|
|
6731
|
+
scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
|
|
6732
|
+
scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
|
|
6733
|
+
scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
|
|
6734
|
+
scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
|
|
6353
6735
|
}
|
|
6354
6736
|
} else if (options.packageManager === "npm") {
|
|
6355
6737
|
scripts.dev = devScript;
|
|
@@ -6357,24 +6739,24 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6357
6739
|
scripts["check-types"] = "npm run check-types --workspaces";
|
|
6358
6740
|
scripts["dev:native"] = "npm run dev --workspace native";
|
|
6359
6741
|
scripts["dev:web"] = "npm run dev --workspace web";
|
|
6360
|
-
scripts["dev:server"] = serverDevScript;
|
|
6742
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6361
6743
|
if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
|
|
6362
6744
|
if (needsDbScripts) {
|
|
6363
|
-
scripts["db:push"] = `npm run db:push --workspace ${
|
|
6364
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${
|
|
6745
|
+
scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
|
|
6746
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
|
|
6365
6747
|
if (options.orm === "prisma") {
|
|
6366
|
-
scripts["db:generate"] = `npm run db:generate --workspace ${
|
|
6367
|
-
scripts["db:migrate"] = `npm run db:migrate --workspace ${
|
|
6748
|
+
scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
|
|
6749
|
+
scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
|
|
6368
6750
|
} else if (options.orm === "drizzle") {
|
|
6369
|
-
scripts["db:generate"] = `npm run db:generate --workspace ${
|
|
6370
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${
|
|
6751
|
+
scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
|
|
6752
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
|
|
6371
6753
|
}
|
|
6372
6754
|
}
|
|
6373
6755
|
if (options.dbSetup === "docker") {
|
|
6374
|
-
scripts["db:start"] = `npm run db:start --workspace ${
|
|
6375
|
-
scripts["db:watch"] = `npm run db:watch --workspace ${
|
|
6376
|
-
scripts["db:stop"] = `npm run db:stop --workspace ${
|
|
6377
|
-
scripts["db:down"] = `npm run db:down --workspace ${
|
|
6756
|
+
scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
|
|
6757
|
+
scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
|
|
6758
|
+
scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
|
|
6759
|
+
scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
|
|
6378
6760
|
}
|
|
6379
6761
|
} else if (options.packageManager === "bun") {
|
|
6380
6762
|
scripts.dev = devScript;
|
|
@@ -6382,24 +6764,24 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6382
6764
|
scripts["check-types"] = "bun run --filter '*' check-types";
|
|
6383
6765
|
scripts["dev:native"] = "bun run --filter native dev";
|
|
6384
6766
|
scripts["dev:web"] = "bun run --filter web dev";
|
|
6385
|
-
scripts["dev:server"] = serverDevScript;
|
|
6767
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6386
6768
|
if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
|
|
6387
6769
|
if (needsDbScripts) {
|
|
6388
|
-
scripts["db:push"] = `bun run --filter ${
|
|
6389
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${
|
|
6770
|
+
scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
|
|
6771
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
|
|
6390
6772
|
if (options.orm === "prisma") {
|
|
6391
|
-
scripts["db:generate"] = `bun run --filter ${
|
|
6392
|
-
scripts["db:migrate"] = `bun run --filter ${
|
|
6773
|
+
scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
|
|
6774
|
+
scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
|
|
6393
6775
|
} else if (options.orm === "drizzle") {
|
|
6394
|
-
scripts["db:generate"] = `bun run --filter ${
|
|
6395
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${
|
|
6776
|
+
scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
|
|
6777
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
|
|
6396
6778
|
}
|
|
6397
6779
|
}
|
|
6398
6780
|
if (options.dbSetup === "docker") {
|
|
6399
|
-
scripts["db:start"] = `bun run --filter ${
|
|
6400
|
-
scripts["db:watch"] = `bun run --filter ${
|
|
6401
|
-
scripts["db:stop"] = `bun run --filter ${
|
|
6402
|
-
scripts["db:down"] = `bun run --filter ${
|
|
6781
|
+
scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
|
|
6782
|
+
scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
|
|
6783
|
+
scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
|
|
6784
|
+
scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
|
|
6403
6785
|
}
|
|
6404
6786
|
}
|
|
6405
6787
|
try {
|
|
@@ -6424,7 +6806,16 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6424
6806
|
if (!await fs.pathExists(serverPackageJsonPath)) return;
|
|
6425
6807
|
const serverPackageJson = await fs.readJson(serverPackageJsonPath);
|
|
6426
6808
|
if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
|
|
6427
|
-
|
|
6809
|
+
await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
|
|
6810
|
+
await updateDbPackageJson(projectDir, options);
|
|
6811
|
+
}
|
|
6812
|
+
async function updateDbPackageJson(projectDir, options) {
|
|
6813
|
+
const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
|
|
6814
|
+
if (!await fs.pathExists(dbPackageJsonPath)) return;
|
|
6815
|
+
const dbPackageJson = await fs.readJson(dbPackageJsonPath);
|
|
6816
|
+
dbPackageJson.name = `@${options.projectName}/db`;
|
|
6817
|
+
if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
|
|
6818
|
+
const scripts = dbPackageJson.scripts;
|
|
6428
6819
|
if (options.database !== "none") {
|
|
6429
6820
|
if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
|
|
6430
6821
|
if (options.orm === "prisma") {
|
|
@@ -6445,7 +6836,21 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6445
6836
|
scripts["db:stop"] = "docker compose stop";
|
|
6446
6837
|
scripts["db:down"] = "docker compose down";
|
|
6447
6838
|
}
|
|
6448
|
-
await fs.writeJson(
|
|
6839
|
+
await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
|
|
6840
|
+
}
|
|
6841
|
+
async function updateAuthPackageJson(projectDir, options) {
|
|
6842
|
+
const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
|
|
6843
|
+
if (!await fs.pathExists(authPackageJsonPath)) return;
|
|
6844
|
+
const authPackageJson = await fs.readJson(authPackageJsonPath);
|
|
6845
|
+
authPackageJson.name = `@${options.projectName}/auth`;
|
|
6846
|
+
await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
|
|
6847
|
+
}
|
|
6848
|
+
async function updateApiPackageJson(projectDir, options) {
|
|
6849
|
+
const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
|
|
6850
|
+
if (!await fs.pathExists(apiPackageJsonPath)) return;
|
|
6851
|
+
const apiPackageJson = await fs.readJson(apiPackageJsonPath);
|
|
6852
|
+
apiPackageJson.name = `@${options.projectName}/api`;
|
|
6853
|
+
await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
|
|
6449
6854
|
}
|
|
6450
6855
|
async function updateConvexPackageJson(projectDir, options) {
|
|
6451
6856
|
const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
|
|
@@ -6461,15 +6866,14 @@ async function updateConvexPackageJson(projectDir, options) {
|
|
|
6461
6866
|
async function createProject(options, cliInput) {
|
|
6462
6867
|
const projectDir = options.projectDir;
|
|
6463
6868
|
const isConvex = options.backend === "convex";
|
|
6869
|
+
const isSelfBackend = options.backend === "self";
|
|
6870
|
+
const needsServerSetup = !isConvex && !isSelfBackend;
|
|
6464
6871
|
try {
|
|
6465
6872
|
await fs.ensureDir(projectDir);
|
|
6466
6873
|
await copyBaseTemplate(projectDir, options);
|
|
6467
6874
|
await setupFrontendTemplates(projectDir, options);
|
|
6468
6875
|
await setupBackendFramework(projectDir, options);
|
|
6469
|
-
if (
|
|
6470
|
-
await setupDbOrmTemplates(projectDir, options);
|
|
6471
|
-
await setupDockerComposeTemplates(projectDir, options);
|
|
6472
|
-
}
|
|
6876
|
+
if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
|
|
6473
6877
|
await setupAuthTemplate(projectDir, options);
|
|
6474
6878
|
if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
|
|
6475
6879
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
@@ -6477,9 +6881,11 @@ async function createProject(options, cliInput) {
|
|
|
6477
6881
|
await setupDeploymentTemplates(projectDir, options);
|
|
6478
6882
|
await setupApi(options);
|
|
6479
6883
|
if (!isConvex) {
|
|
6480
|
-
|
|
6884
|
+
if (needsServerSetup) {
|
|
6885
|
+
await setupBackendDependencies(options);
|
|
6886
|
+
await setupRuntime(options);
|
|
6887
|
+
}
|
|
6481
6888
|
await setupDatabase(options, cliInput);
|
|
6482
|
-
await setupRuntime(options);
|
|
6483
6889
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
|
|
6484
6890
|
}
|
|
6485
6891
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
@@ -6488,6 +6894,7 @@ async function createProject(options, cliInput) {
|
|
|
6488
6894
|
await handleExtras(projectDir, options);
|
|
6489
6895
|
await setupEnvironmentVariables(options);
|
|
6490
6896
|
await updatePackageConfigurations(projectDir, options);
|
|
6897
|
+
await setupCatalogs(projectDir, options);
|
|
6491
6898
|
await setupWebDeploy(options);
|
|
6492
6899
|
await setupServerDeploy(options);
|
|
6493
6900
|
await createReadme(projectDir, options);
|