create-better-t-stack 2.49.1 → 2.50.0-canary.08568a05
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 +2 -2
- package/dist/index.js +1 -1
- package/dist/{src-CKCxH6aF.js → src-DfbhNFZ9.js} +527 -261
- package/package.json +2 -1
- package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +21 -0
- package/templates/api/orpc/server/_gitignore +34 -0
- package/templates/api/orpc/server/package.json.hbs +24 -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/server/_gitignore +34 -0
- package/templates/api/trpc/server/package.json.hbs +23 -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 +13 -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 +24 -0
- package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +7 -7
- package/templates/auth/better-auth/server/base/tsconfig.json.hbs +13 -0
- package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +1 -0
- package/templates/backend/server/{server-base → base}/package.json.hbs +0 -1
- package/templates/backend/server/{server-base → base}/tsconfig.json.hbs +5 -10
- package/templates/backend/server/base/tsdown.config.ts.hbs +14 -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/base/tsconfig.base.json +23 -0
- package/templates/db/base/_gitignore +34 -0
- package/templates/db/base/package.json.hbs +23 -0
- package/templates/db/base/tsconfig.json.hbs +13 -0
- package/templates/db/base/tsdown.config.ts.hbs +7 -0
- package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +7 -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 +7 -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 +7 -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 +5 -1
- package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
- package/templates/db/prisma/mysql/prisma.config.ts.hbs +5 -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 +7 -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 +5 -1
- package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
- package/templates/deploy/wrangler/web/react/tanstack-start/wrangler.jsonc.hbs +1 -1
- 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 +7 -7
- 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/frontend/react/next/package.json.hbs +1 -1
- package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
- package/templates/frontend/react/tanstack-start/package.json.hbs +7 -7
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +5 -5
- package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +1 -1
- 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
|
|
@@ -65,12 +66,13 @@ const dependencyVersionMap = {
|
|
|
65
66
|
"@better-auth/expo": "^1.3.13",
|
|
66
67
|
"@clerk/nextjs": "^6.31.5",
|
|
67
68
|
"@clerk/clerk-react": "^5.45.0",
|
|
68
|
-
"@clerk/tanstack-react-start": "^0.
|
|
69
|
+
"@clerk/tanstack-react-start": "^0.25.1",
|
|
69
70
|
"@clerk/clerk-expo": "^2.14.25",
|
|
70
71
|
"drizzle-orm": "^0.44.2",
|
|
71
72
|
"drizzle-kit": "^0.31.2",
|
|
72
73
|
"@planetscale/database": "^1.19.0",
|
|
73
74
|
"@libsql/client": "^0.15.9",
|
|
75
|
+
libsql: "^0.5.22",
|
|
74
76
|
"@neondatabase/serverless": "^1.0.1",
|
|
75
77
|
pg: "^8.14.1",
|
|
76
78
|
"@types/pg": "^8.11.11",
|
|
@@ -123,6 +125,7 @@ const dependencyVersionMap = {
|
|
|
123
125
|
"@trpc/tanstack-react-query": "^11.5.0",
|
|
124
126
|
"@trpc/server": "^11.5.0",
|
|
125
127
|
"@trpc/client": "^11.5.0",
|
|
128
|
+
"next": "15.5.4",
|
|
126
129
|
convex: "^1.27.0",
|
|
127
130
|
"@convex-dev/react-query": "^0.0.0-alpha.8",
|
|
128
131
|
"convex-svelte": "^0.0.11",
|
|
@@ -138,15 +141,17 @@ const dependencyVersionMap = {
|
|
|
138
141
|
"@tanstack/solid-query": "^5.87.4",
|
|
139
142
|
"@tanstack/solid-query-devtools": "^5.87.4",
|
|
140
143
|
"@tanstack/solid-router-devtools": "^1.131.44",
|
|
141
|
-
wrangler: "^4.
|
|
142
|
-
"@cloudflare/vite-plugin": "^1.
|
|
144
|
+
wrangler: "^4.40.3",
|
|
145
|
+
"@cloudflare/vite-plugin": "^1.13.8",
|
|
143
146
|
"@opennextjs/cloudflare": "^1.6.5",
|
|
144
147
|
"nitro-cloudflare-dev": "^0.2.2",
|
|
145
148
|
"@sveltejs/adapter-cloudflare": "^7.2.1",
|
|
146
149
|
"@cloudflare/workers-types": "^4.20250822.0",
|
|
147
|
-
alchemy: "^0.
|
|
150
|
+
alchemy: "^0.70.0",
|
|
148
151
|
nitropack: "^2.12.4",
|
|
149
|
-
dotenv: "^17.2.
|
|
152
|
+
dotenv: "^17.2.2",
|
|
153
|
+
tsdown: "^0.15.5",
|
|
154
|
+
zod: "^4.1.11",
|
|
150
155
|
"@polar-sh/better-auth": "^1.1.3",
|
|
151
156
|
"@polar-sh/sdk": "^0.34.16"
|
|
152
157
|
};
|
|
@@ -195,9 +200,9 @@ const BackendSchema = z.enum([
|
|
|
195
200
|
"hono",
|
|
196
201
|
"express",
|
|
197
202
|
"fastify",
|
|
198
|
-
"next",
|
|
199
203
|
"elysia",
|
|
200
204
|
"convex",
|
|
205
|
+
"self",
|
|
201
206
|
"none"
|
|
202
207
|
]).describe("Backend framework");
|
|
203
208
|
const RuntimeSchema = z.enum([
|
|
@@ -343,6 +348,22 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
343
348
|
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
349
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
345
350
|
}
|
|
351
|
+
const FULLSTACK_FRONTENDS$1 = [
|
|
352
|
+
"next",
|
|
353
|
+
"nuxt",
|
|
354
|
+
"svelte",
|
|
355
|
+
"tanstack-start"
|
|
356
|
+
];
|
|
357
|
+
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
358
|
+
const backend = config.backend || options.backend;
|
|
359
|
+
const frontends = config.frontend || options.frontend || [];
|
|
360
|
+
if (backend === "self") {
|
|
361
|
+
if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack) requires a fullstack-capable frontend. Please use --frontend with one of: next, nuxt, svelte, tanstack-start");
|
|
362
|
+
if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
|
|
363
|
+
}
|
|
364
|
+
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
365
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) is only compatible with fullstack-capable frontends: next, nuxt, svelte, tanstack-start. Please choose a different backend or use a fullstack frontend.");
|
|
366
|
+
}
|
|
346
367
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
347
368
|
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
369
|
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 +685,39 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
|
664
685
|
|
|
665
686
|
//#endregion
|
|
666
687
|
//#region src/prompts/backend.ts
|
|
688
|
+
const FULLSTACK_FRONTENDS = [
|
|
689
|
+
"next",
|
|
690
|
+
"nuxt",
|
|
691
|
+
"svelte",
|
|
692
|
+
"tanstack-start"
|
|
693
|
+
];
|
|
667
694
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
668
695
|
if (backendFramework !== void 0) return backendFramework;
|
|
669
696
|
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
|
-
];
|
|
697
|
+
const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
|
|
698
|
+
const backendOptions = [];
|
|
699
|
+
if (hasFullstackFrontend) backendOptions.push({
|
|
700
|
+
value: "self",
|
|
701
|
+
label: "Self (Fullstack)",
|
|
702
|
+
hint: "Use frontend's built-in backend capabilities"
|
|
703
|
+
});
|
|
704
|
+
backendOptions.push({
|
|
705
|
+
value: "hono",
|
|
706
|
+
label: "Hono",
|
|
707
|
+
hint: "Lightweight, ultrafast web framework"
|
|
708
|
+
}, {
|
|
709
|
+
value: "express",
|
|
710
|
+
label: "Express",
|
|
711
|
+
hint: "Fast, unopinionated, minimalist web framework for Node.js"
|
|
712
|
+
}, {
|
|
713
|
+
value: "fastify",
|
|
714
|
+
label: "Fastify",
|
|
715
|
+
hint: "Fast, low-overhead web framework for Node.js"
|
|
716
|
+
}, {
|
|
717
|
+
value: "elysia",
|
|
718
|
+
label: "Elysia",
|
|
719
|
+
hint: "Ergonomic web framework for building backend servers"
|
|
720
|
+
});
|
|
697
721
|
if (!hasIncompatibleFrontend) backendOptions.push({
|
|
698
722
|
value: "convex",
|
|
699
723
|
label: "Convex",
|
|
@@ -707,7 +731,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
707
731
|
const response = await select({
|
|
708
732
|
message: "Select backend",
|
|
709
733
|
options: backendOptions,
|
|
710
|
-
initialValue: DEFAULT_CONFIG.backend
|
|
734
|
+
initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
|
|
711
735
|
});
|
|
712
736
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
713
737
|
return response;
|
|
@@ -1086,9 +1110,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
|
1086
1110
|
//#endregion
|
|
1087
1111
|
//#region src/prompts/runtime.ts
|
|
1088
1112
|
async function getRuntimeChoice(runtime, backend) {
|
|
1089
|
-
if (backend === "convex" || backend === "none") return "none";
|
|
1113
|
+
if (backend === "convex" || backend === "none" || backend === "self") return "none";
|
|
1090
1114
|
if (runtime !== void 0) return runtime;
|
|
1091
|
-
if (backend === "next") return "node";
|
|
1092
1115
|
const runtimeOptions = [{
|
|
1093
1116
|
value: "bun",
|
|
1094
1117
|
label: "Bun",
|
|
@@ -1375,7 +1398,7 @@ const getLatestCLIVersion = () => {
|
|
|
1375
1398
|
*/
|
|
1376
1399
|
function isTelemetryEnabled() {
|
|
1377
1400
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1378
|
-
const BTS_TELEMETRY = "
|
|
1401
|
+
const BTS_TELEMETRY = "0";
|
|
1379
1402
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1380
1403
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1381
1404
|
return true;
|
|
@@ -1383,8 +1406,8 @@ function isTelemetryEnabled() {
|
|
|
1383
1406
|
|
|
1384
1407
|
//#endregion
|
|
1385
1408
|
//#region src/utils/analytics.ts
|
|
1386
|
-
const POSTHOG_API_KEY = "
|
|
1387
|
-
const POSTHOG_HOST = "
|
|
1409
|
+
const POSTHOG_API_KEY = "random";
|
|
1410
|
+
const POSTHOG_HOST = "random";
|
|
1388
1411
|
function generateSessionId() {
|
|
1389
1412
|
const rand = Math.random().toString(36).slice(2);
|
|
1390
1413
|
return `cli_${Date.now().toString(36)}${rand}`;
|
|
@@ -1765,8 +1788,8 @@ function validateBackendConstraints(config, providedFlags, options) {
|
|
|
1765
1788
|
].includes(f));
|
|
1766
1789
|
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
1790
|
}
|
|
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
|
|
1791
|
+
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
|
|
1792
|
+
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
1793
|
}
|
|
1771
1794
|
if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
|
|
1772
1795
|
const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
|
|
@@ -1796,6 +1819,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1796
1819
|
validateFrontendConstraints(config, providedFlags);
|
|
1797
1820
|
validateApiConstraints(config, options);
|
|
1798
1821
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
1822
|
+
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
1799
1823
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1800
1824
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
|
|
1801
1825
|
if (config.addons && config.addons.length > 0) {
|
|
@@ -1849,6 +1873,7 @@ const CORE_STACK_FLAGS = new Set([
|
|
|
1849
1873
|
"examples",
|
|
1850
1874
|
"auth",
|
|
1851
1875
|
"dbSetup",
|
|
1876
|
+
"payments",
|
|
1852
1877
|
"api",
|
|
1853
1878
|
"webDeploy",
|
|
1854
1879
|
"serverDeploy"
|
|
@@ -1992,15 +2017,17 @@ const addPackageDependency = async (opts) => {
|
|
|
1992
2017
|
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
1993
2018
|
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
1994
2019
|
for (const pkgName of dependencies) {
|
|
1995
|
-
const version =
|
|
2020
|
+
const version = dependencyVersionMap[pkgName];
|
|
1996
2021
|
if (version) pkgJson.dependencies[pkgName] = version;
|
|
1997
2022
|
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
1998
2023
|
}
|
|
1999
2024
|
for (const pkgName of devDependencies) {
|
|
2000
|
-
const version =
|
|
2025
|
+
const version = dependencyVersionMap[pkgName];
|
|
2001
2026
|
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
2002
2027
|
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
2003
2028
|
}
|
|
2029
|
+
for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
|
|
2030
|
+
for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
|
|
2004
2031
|
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
2005
2032
|
};
|
|
2006
2033
|
|
|
@@ -2655,8 +2682,12 @@ async function processTemplate(srcPath, destPath, context) {
|
|
|
2655
2682
|
}
|
|
2656
2683
|
handlebars.registerHelper("eq", (a, b) => a === b);
|
|
2657
2684
|
handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
2658
|
-
handlebars.registerHelper("and", (
|
|
2659
|
-
|
|
2685
|
+
handlebars.registerHelper("and", (...args) => {
|
|
2686
|
+
return args.slice(0, -1).every((value) => value);
|
|
2687
|
+
});
|
|
2688
|
+
handlebars.registerHelper("or", (...args) => {
|
|
2689
|
+
return args.slice(0, -1).some((value) => value);
|
|
2690
|
+
});
|
|
2660
2691
|
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
2661
2692
|
|
|
2662
2693
|
//#endregion
|
|
@@ -2718,6 +2749,10 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2718
2749
|
const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
|
|
2719
2750
|
if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
|
|
2720
2751
|
}
|
|
2752
|
+
if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
|
|
2753
|
+
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
|
|
2754
|
+
if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
|
|
2755
|
+
}
|
|
2721
2756
|
}
|
|
2722
2757
|
} else if (hasNuxtWeb) {
|
|
2723
2758
|
const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
|
|
@@ -2758,35 +2793,52 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2758
2793
|
}
|
|
2759
2794
|
}
|
|
2760
2795
|
}
|
|
2761
|
-
async function
|
|
2762
|
-
if (context.
|
|
2796
|
+
async function setupApiPackage(projectDir, context) {
|
|
2797
|
+
if (context.api === "none") return;
|
|
2798
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
2799
|
+
await fs.ensureDir(apiPackageDir);
|
|
2800
|
+
const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
|
|
2801
|
+
if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
|
|
2802
|
+
}
|
|
2803
|
+
async function setupDbPackage(projectDir, context) {
|
|
2804
|
+
if (context.database === "none" || context.orm === "none") return;
|
|
2805
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
2806
|
+
await fs.ensureDir(dbPackageDir);
|
|
2807
|
+
const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
|
|
2808
|
+
if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
|
|
2809
|
+
const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
|
|
2810
|
+
if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
|
|
2811
|
+
}
|
|
2812
|
+
async function setupConvexBackend(projectDir, context) {
|
|
2813
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2814
|
+
if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
|
|
2815
|
+
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2816
|
+
const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
|
|
2817
|
+
await fs.ensureDir(convexBackendDestDir);
|
|
2818
|
+
if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
|
|
2819
|
+
}
|
|
2820
|
+
async function setupServerApp(projectDir, context) {
|
|
2763
2821
|
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
2822
|
await fs.ensureDir(serverAppDir);
|
|
2773
|
-
const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/
|
|
2823
|
+
const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
|
|
2774
2824
|
if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
|
|
2775
2825
|
const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
|
|
2776
2826
|
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
2827
|
}
|
|
2784
|
-
async function
|
|
2785
|
-
if (context.backend === "
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2828
|
+
async function setupBackendFramework(projectDir, context) {
|
|
2829
|
+
if (context.backend === "none") return;
|
|
2830
|
+
if (context.backend === "convex") {
|
|
2831
|
+
await setupConvexBackend(projectDir, context);
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (context.backend === "self") {
|
|
2835
|
+
await setupApiPackage(projectDir, context);
|
|
2836
|
+
await setupDbPackage(projectDir, context);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
await setupServerApp(projectDir, context);
|
|
2840
|
+
await setupApiPackage(projectDir, context);
|
|
2841
|
+
await setupDbPackage(projectDir, context);
|
|
2790
2842
|
}
|
|
2791
2843
|
async function setupAuthTemplate(projectDir, context) {
|
|
2792
2844
|
if (!context.auth || context.auth === "none") return;
|
|
@@ -2866,21 +2918,21 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2866
2918
|
}
|
|
2867
2919
|
return;
|
|
2868
2920
|
}
|
|
2869
|
-
if (serverAppDirExists && context.backend !== "convex") {
|
|
2921
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
2922
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
2923
|
+
await fs.ensureDir(authPackageDir);
|
|
2870
2924
|
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
|
-
}
|
|
2925
|
+
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
|
|
2876
2926
|
if (context.orm !== "none" && context.database !== "none") {
|
|
2927
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
2928
|
+
await fs.ensureDir(dbPackageDir);
|
|
2877
2929
|
const orm = context.orm;
|
|
2878
2930
|
const db = context.database;
|
|
2879
2931
|
let authDbSrc = "";
|
|
2880
2932
|
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
|
|
2881
2933
|
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
|
|
2882
2934
|
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,
|
|
2935
|
+
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
|
|
2884
2936
|
}
|
|
2885
2937
|
}
|
|
2886
2938
|
if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
|
|
@@ -2896,6 +2948,10 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2896
2948
|
if (reactFramework) {
|
|
2897
2949
|
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2898
2950
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2951
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
2952
|
+
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
|
|
2953
|
+
if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
|
|
2954
|
+
}
|
|
2899
2955
|
}
|
|
2900
2956
|
} else if (hasNuxtWeb) {
|
|
2901
2957
|
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
@@ -2926,9 +2982,11 @@ async function setupPaymentsTemplate(projectDir, context) {
|
|
|
2926
2982
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
2927
2983
|
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
2928
2984
|
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
2929
|
-
if (serverAppDirExists && context.backend !== "convex") {
|
|
2985
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
2986
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
2987
|
+
await fs.ensureDir(authPackageDir);
|
|
2930
2988
|
const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
|
|
2931
|
-
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc,
|
|
2989
|
+
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
|
|
2932
2990
|
}
|
|
2933
2991
|
const hasReactWeb = context.frontend.some((f) => [
|
|
2934
2992
|
"tanstack-router",
|
|
@@ -3004,17 +3062,19 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3004
3062
|
for (const example of context.examples) {
|
|
3005
3063
|
if (example === "none") continue;
|
|
3006
3064
|
const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
|
|
3007
|
-
if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
|
|
3065
|
+
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
|
|
3008
3066
|
const exampleServerSrc = path.join(exampleBaseDir, "server");
|
|
3009
|
-
if (
|
|
3010
|
-
const
|
|
3011
|
-
|
|
3067
|
+
if (context.api !== "none") {
|
|
3068
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
3069
|
+
await fs.ensureDir(apiPackageDir);
|
|
3070
|
+
const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
|
|
3071
|
+
if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
|
|
3012
3072
|
}
|
|
3013
3073
|
if (context.orm !== "none" && context.database !== "none") {
|
|
3014
|
-
const
|
|
3015
|
-
|
|
3074
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3075
|
+
await fs.ensureDir(dbPackageDir);
|
|
3016
3076
|
const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
|
|
3017
|
-
if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc,
|
|
3077
|
+
if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
|
|
3018
3078
|
}
|
|
3019
3079
|
}
|
|
3020
3080
|
if (webAppDirExists) {
|
|
@@ -3034,6 +3094,10 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3034
3094
|
if (reactFramework) {
|
|
3035
3095
|
const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
|
|
3036
3096
|
if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
|
|
3097
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
3098
|
+
const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
|
|
3099
|
+
if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
|
|
3100
|
+
}
|
|
3037
3101
|
}
|
|
3038
3102
|
}
|
|
3039
3103
|
} else if (hasNuxtWeb) {
|
|
@@ -3081,9 +3145,9 @@ async function handleExtras(projectDir, context) {
|
|
|
3081
3145
|
}
|
|
3082
3146
|
async function setupDockerComposeTemplates(projectDir, context) {
|
|
3083
3147
|
if (context.dbSetup !== "docker" || context.database === "none") return;
|
|
3084
|
-
const
|
|
3148
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3085
3149
|
const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
|
|
3086
|
-
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir,
|
|
3150
|
+
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
|
|
3087
3151
|
}
|
|
3088
3152
|
async function setupDeploymentTemplates(projectDir, context) {
|
|
3089
3153
|
if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
|
|
@@ -3504,8 +3568,8 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3504
3568
|
await addPackageDependency({
|
|
3505
3569
|
devDependencies: [
|
|
3506
3570
|
"alchemy",
|
|
3507
|
-
"
|
|
3508
|
-
"
|
|
3571
|
+
"dotenv",
|
|
3572
|
+
"@cloudflare/vite-plugin"
|
|
3509
3573
|
],
|
|
3510
3574
|
projectDir: webAppDir
|
|
3511
3575
|
});
|
|
@@ -3533,17 +3597,6 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3533
3597
|
defaultImport: "alchemy"
|
|
3534
3598
|
});
|
|
3535
3599
|
else alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
|
|
3536
|
-
const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
|
|
3537
|
-
let reactPluginIdentifier = "viteReact";
|
|
3538
|
-
if (!reactImport) sourceFile.addImportDeclaration({
|
|
3539
|
-
moduleSpecifier: "@vitejs/plugin-react",
|
|
3540
|
-
defaultImport: "viteReact"
|
|
3541
|
-
});
|
|
3542
|
-
else {
|
|
3543
|
-
const defaultImport = reactImport.getDefaultImport();
|
|
3544
|
-
if (defaultImport) reactPluginIdentifier = defaultImport.getText();
|
|
3545
|
-
else reactImport.setDefaultImport("viteReact");
|
|
3546
|
-
}
|
|
3547
3600
|
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
3548
3601
|
if (!exportAssignment) return;
|
|
3549
3602
|
const defineConfigCall = exportAssignment.getExpression();
|
|
@@ -3551,47 +3604,11 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3551
3604
|
let configObject = defineConfigCall.getArguments()[0];
|
|
3552
3605
|
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
3553
3606
|
if (Node.isObjectLiteralExpression(configObject)) {
|
|
3554
|
-
if (!configObject.getProperty("build")) configObject.addPropertyAssignment({
|
|
3555
|
-
name: "build",
|
|
3556
|
-
initializer: `{
|
|
3557
|
-
target: "esnext",
|
|
3558
|
-
rollupOptions: {
|
|
3559
|
-
external: ["node:async_hooks", "cloudflare:workers"],
|
|
3560
|
-
},
|
|
3561
|
-
}`
|
|
3562
|
-
});
|
|
3563
3607
|
const pluginsProperty = configObject.getProperty("plugins");
|
|
3564
3608
|
if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
|
|
3565
3609
|
const initializer = pluginsProperty.getInitializer();
|
|
3566
3610
|
if (Node.isArrayLiteralExpression(initializer)) {
|
|
3567
|
-
if (!initializer.getElements().some((el) => el.getText().includes("alchemy"))) initializer.addElement("alchemy()");
|
|
3568
|
-
const tanstackElements = initializer.getElements().filter((el) => el.getText().includes("tanstackStart"));
|
|
3569
|
-
let needsReactPlugin = false;
|
|
3570
|
-
tanstackElements.forEach((element) => {
|
|
3571
|
-
if (Node.isCallExpression(element)) {
|
|
3572
|
-
const args = element.getArguments();
|
|
3573
|
-
if (args.length === 0) {
|
|
3574
|
-
element.addArgument(`{
|
|
3575
|
-
target: "cloudflare-module",
|
|
3576
|
-
customViteReactPlugin: true,
|
|
3577
|
-
}`);
|
|
3578
|
-
needsReactPlugin = true;
|
|
3579
|
-
} else if (args.length === 1 && Node.isObjectLiteralExpression(args[0])) {
|
|
3580
|
-
const configObj = args[0];
|
|
3581
|
-
if (!configObj.getProperty("target")) configObj.addPropertyAssignment({
|
|
3582
|
-
name: "target",
|
|
3583
|
-
initializer: "\"cloudflare-module\""
|
|
3584
|
-
});
|
|
3585
|
-
if (!!!configObj.getProperty("customViteReactPlugin")) configObj.addPropertyAssignment({
|
|
3586
|
-
name: "customViteReactPlugin",
|
|
3587
|
-
initializer: "true"
|
|
3588
|
-
});
|
|
3589
|
-
needsReactPlugin = true;
|
|
3590
|
-
}
|
|
3591
|
-
}
|
|
3592
|
-
});
|
|
3593
|
-
const hasReactPlugin = initializer.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier);
|
|
3594
|
-
if (needsReactPlugin && !hasReactPlugin) initializer.addElement(`${reactPluginIdentifier}()`);
|
|
3611
|
+
if (!initializer.getElements().some((el) => el.getText().includes("alchemy("))) initializer.addElement("alchemy()");
|
|
3595
3612
|
}
|
|
3596
3613
|
} else configObject.addPropertyAssignment({
|
|
3597
3614
|
name: "plugins",
|
|
@@ -3602,16 +3619,6 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3602
3619
|
} catch (error) {
|
|
3603
3620
|
console.warn("Failed to update vite.config.ts:", error);
|
|
3604
3621
|
}
|
|
3605
|
-
const nitroConfigPath = path.join(webAppDir, "nitro.config.ts");
|
|
3606
|
-
await fs.writeFile(nitroConfigPath, `import { defineNitroConfig } from "nitropack/config";
|
|
3607
|
-
|
|
3608
|
-
export default defineNitroConfig({
|
|
3609
|
-
preset: "cloudflare-module",
|
|
3610
|
-
cloudflare: {
|
|
3611
|
-
nodeCompat: true,
|
|
3612
|
-
},
|
|
3613
|
-
});
|
|
3614
|
-
`, "utf-8");
|
|
3615
3622
|
}
|
|
3616
3623
|
|
|
3617
3624
|
//#endregion
|
|
@@ -3778,7 +3785,7 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
|
3778
3785
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3779
3786
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3780
3787
|
await addPackageDependency({
|
|
3781
|
-
devDependencies: ["wrangler"],
|
|
3788
|
+
devDependencies: ["wrangler", "@cloudflare/vite-plugin"],
|
|
3782
3789
|
projectDir: webAppDir
|
|
3783
3790
|
});
|
|
3784
3791
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3795,6 +3802,12 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
|
3795
3802
|
if (!await fs.pathExists(viteConfigPath)) return;
|
|
3796
3803
|
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
3797
3804
|
if (!sourceFile) return;
|
|
3805
|
+
const cfImport = sourceFile.getImportDeclaration("@cloudflare/vite-plugin");
|
|
3806
|
+
if (!cfImport) sourceFile.addImportDeclaration({
|
|
3807
|
+
moduleSpecifier: "@cloudflare/vite-plugin",
|
|
3808
|
+
namedImports: [{ name: "cloudflare" }]
|
|
3809
|
+
});
|
|
3810
|
+
else if (!cfImport.getNamedImports().some((ni) => ni.getName() === "cloudflare")) cfImport.addNamedImport({ name: "cloudflare" });
|
|
3798
3811
|
const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
|
|
3799
3812
|
let reactPluginIdentifier = "viteReact";
|
|
3800
3813
|
if (!reactImport) sourceFile.addImportDeclaration({
|
|
@@ -3814,10 +3827,7 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
|
3814
3827
|
const configObj = defineCall.getArguments()[0];
|
|
3815
3828
|
if (!configObj) return;
|
|
3816
3829
|
const pluginsArray = ensureArrayProperty(configObj, "plugins");
|
|
3817
|
-
|
|
3818
|
-
const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\", customViteReactPlugin: true })";
|
|
3819
|
-
if (tanstackPluginIndex === -1) pluginsArray.addElement(tanstackPluginText);
|
|
3820
|
-
else pluginsArray.getElements()[tanstackPluginIndex].replaceWithText(tanstackPluginText);
|
|
3830
|
+
if (!pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("))) pluginsArray.insertElement(0, "cloudflare({ viteEnvironment: { name: 'ssr' } })");
|
|
3821
3831
|
if (!pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier)) {
|
|
3822
3832
|
const nextIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart(")) + 1;
|
|
3823
3833
|
if (nextIndex > 0) pluginsArray.insertElement(nextIndex, `${reactPluginIdentifier}()`);
|
|
@@ -3955,18 +3965,137 @@ async function addDeploymentToProject(input) {
|
|
|
3955
3965
|
}
|
|
3956
3966
|
}
|
|
3957
3967
|
|
|
3968
|
+
//#endregion
|
|
3969
|
+
//#region src/utils/setup-catalogs.ts
|
|
3970
|
+
async function setupCatalogs(projectDir, options) {
|
|
3971
|
+
if (options.packageManager === "npm") return;
|
|
3972
|
+
const packagePaths = [
|
|
3973
|
+
"apps/server",
|
|
3974
|
+
"apps/web",
|
|
3975
|
+
"packages/api",
|
|
3976
|
+
"packages/db",
|
|
3977
|
+
"packages/auth",
|
|
3978
|
+
"packages/backend"
|
|
3979
|
+
];
|
|
3980
|
+
const packagesInfo = [];
|
|
3981
|
+
for (const pkgPath of packagePaths) {
|
|
3982
|
+
const fullPath = path.join(projectDir, pkgPath);
|
|
3983
|
+
const pkgJsonPath = path.join(fullPath, "package.json");
|
|
3984
|
+
if (await fs.pathExists(pkgJsonPath)) {
|
|
3985
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
3986
|
+
packagesInfo.push({
|
|
3987
|
+
path: fullPath,
|
|
3988
|
+
dependencies: pkgJson.dependencies || {},
|
|
3989
|
+
devDependencies: pkgJson.devDependencies || {}
|
|
3990
|
+
});
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
|
|
3994
|
+
if (Object.keys(catalog).length === 0) return;
|
|
3995
|
+
if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
|
|
3996
|
+
else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
|
|
3997
|
+
await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
|
|
3998
|
+
}
|
|
3999
|
+
function findDuplicateDependencies(packagesInfo, projectName) {
|
|
4000
|
+
const depCount = /* @__PURE__ */ new Map();
|
|
4001
|
+
const projectScope = `@${projectName}/`;
|
|
4002
|
+
for (const pkg of packagesInfo) {
|
|
4003
|
+
const allDeps = {
|
|
4004
|
+
...pkg.dependencies,
|
|
4005
|
+
...pkg.devDependencies
|
|
4006
|
+
};
|
|
4007
|
+
for (const [depName, version] of Object.entries(allDeps)) {
|
|
4008
|
+
if (depName.startsWith(projectScope)) continue;
|
|
4009
|
+
if (version.startsWith("workspace:")) continue;
|
|
4010
|
+
const existing = depCount.get(depName);
|
|
4011
|
+
if (existing) existing.packages.push(pkg.path);
|
|
4012
|
+
else depCount.set(depName, {
|
|
4013
|
+
version,
|
|
4014
|
+
packages: [pkg.path]
|
|
4015
|
+
});
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
const catalog = {};
|
|
4019
|
+
for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
|
|
4020
|
+
return catalog;
|
|
4021
|
+
}
|
|
4022
|
+
async function setupBunCatalogs(projectDir, catalog) {
|
|
4023
|
+
const rootPkgJsonPath = path.join(projectDir, "package.json");
|
|
4024
|
+
const rootPkgJson = await fs.readJson(rootPkgJsonPath);
|
|
4025
|
+
if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
|
|
4026
|
+
if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
|
|
4027
|
+
packages: rootPkgJson.workspaces,
|
|
4028
|
+
catalog
|
|
4029
|
+
};
|
|
4030
|
+
else if (typeof rootPkgJson.workspaces === "object") {
|
|
4031
|
+
if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
|
|
4032
|
+
rootPkgJson.workspaces.catalog = {
|
|
4033
|
+
...rootPkgJson.workspaces.catalog,
|
|
4034
|
+
...catalog
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
|
|
4038
|
+
}
|
|
4039
|
+
async function setupPnpmCatalogs(projectDir, catalog) {
|
|
4040
|
+
const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
|
|
4041
|
+
if (!await fs.pathExists(workspaceYamlPath)) return;
|
|
4042
|
+
const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
|
|
4043
|
+
const workspaceYaml = yaml.parse(workspaceContent);
|
|
4044
|
+
if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
|
|
4045
|
+
workspaceYaml.catalog = {
|
|
4046
|
+
...workspaceYaml.catalog,
|
|
4047
|
+
...catalog
|
|
4048
|
+
};
|
|
4049
|
+
await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
|
|
4050
|
+
}
|
|
4051
|
+
async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
|
|
4052
|
+
for (const pkg of packagesInfo) {
|
|
4053
|
+
const pkgJsonPath = path.join(pkg.path, "package.json");
|
|
4054
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4055
|
+
let updated = false;
|
|
4056
|
+
if (pkgJson.dependencies) {
|
|
4057
|
+
for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
|
|
4058
|
+
pkgJson.dependencies[depName] = "catalog:";
|
|
4059
|
+
updated = true;
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
if (pkgJson.devDependencies) {
|
|
4063
|
+
for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
|
|
4064
|
+
pkgJson.devDependencies[depName] = "catalog:";
|
|
4065
|
+
updated = true;
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
|
|
3958
4072
|
//#endregion
|
|
3959
4073
|
//#region src/helpers/addons/examples-setup.ts
|
|
3960
4074
|
async function setupExamples(config) {
|
|
3961
|
-
const { examples, frontend, backend, projectDir } = config;
|
|
4075
|
+
const { examples, frontend, backend, projectDir, orm } = config;
|
|
3962
4076
|
if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
|
|
4077
|
+
const apiDir = path.join(projectDir, "packages/api");
|
|
4078
|
+
if (await fs.pathExists(apiDir) && backend !== "none") {
|
|
4079
|
+
if (orm === "drizzle") await addPackageDependency({
|
|
4080
|
+
dependencies: ["drizzle-orm"],
|
|
4081
|
+
projectDir: apiDir
|
|
4082
|
+
});
|
|
4083
|
+
else if (orm === "prisma") await addPackageDependency({
|
|
4084
|
+
dependencies: ["@prisma/client"],
|
|
4085
|
+
projectDir: apiDir
|
|
4086
|
+
});
|
|
4087
|
+
else if (orm === "mongoose") await addPackageDependency({
|
|
4088
|
+
dependencies: ["mongoose"],
|
|
4089
|
+
projectDir: apiDir
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
3963
4092
|
if (examples.includes("ai")) {
|
|
3964
4093
|
const webClientDir = path.join(projectDir, "apps/web");
|
|
3965
4094
|
const nativeClientDir = path.join(projectDir, "apps/native");
|
|
3966
|
-
const
|
|
4095
|
+
const apiDir$1 = path.join(projectDir, "packages/api");
|
|
3967
4096
|
const webClientDirExists = await fs.pathExists(webClientDir);
|
|
3968
4097
|
const nativeClientDirExists = await fs.pathExists(nativeClientDir);
|
|
3969
|
-
const
|
|
4098
|
+
const apiDirExists = await fs.pathExists(apiDir$1);
|
|
3970
4099
|
const hasNuxt = frontend.includes("nuxt");
|
|
3971
4100
|
const hasSvelte = frontend.includes("svelte");
|
|
3972
4101
|
const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
|
|
@@ -3987,9 +4116,9 @@ async function setupExamples(config) {
|
|
|
3987
4116
|
dependencies: ["ai", "@ai-sdk/react"],
|
|
3988
4117
|
projectDir: nativeClientDir
|
|
3989
4118
|
});
|
|
3990
|
-
if (
|
|
4119
|
+
if (apiDirExists && backend !== "none") await addPackageDependency({
|
|
3991
4120
|
dependencies: ["ai", "@ai-sdk/google"],
|
|
3992
|
-
projectDir:
|
|
4121
|
+
projectDir: apiDir$1
|
|
3993
4122
|
});
|
|
3994
4123
|
}
|
|
3995
4124
|
}
|
|
@@ -4105,32 +4234,57 @@ function getConvexDependencies(frontend) {
|
|
|
4105
4234
|
return deps;
|
|
4106
4235
|
}
|
|
4107
4236
|
async function setupApi(config) {
|
|
4108
|
-
const { api, projectName, frontend, backend, packageManager, projectDir } = config;
|
|
4237
|
+
const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
|
|
4109
4238
|
const isConvex = backend === "convex";
|
|
4110
4239
|
const webDir = path.join(projectDir, "apps/web");
|
|
4111
4240
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
4112
4241
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4113
4242
|
const webDirExists = await fs.pathExists(webDir);
|
|
4114
4243
|
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4115
|
-
|
|
4244
|
+
await fs.pathExists(serverDir);
|
|
4116
4245
|
const frontendType = getFrontendType(frontend);
|
|
4117
4246
|
if (!isConvex && api !== "none") {
|
|
4118
4247
|
const apiDeps = getApiDependencies(api, frontendType);
|
|
4119
|
-
|
|
4248
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
4249
|
+
if (apiDeps.server) {
|
|
4120
4250
|
await addPackageDependency({
|
|
4121
4251
|
dependencies: apiDeps.server.dependencies,
|
|
4122
|
-
projectDir:
|
|
4252
|
+
projectDir: apiPackageDir
|
|
4253
|
+
});
|
|
4254
|
+
const frameworkDeps = [];
|
|
4255
|
+
if (backend === "hono") frameworkDeps.push("hono");
|
|
4256
|
+
else if (backend === "elysia") frameworkDeps.push("elysia");
|
|
4257
|
+
else if (backend === "express") frameworkDeps.push("express", "@types/express");
|
|
4258
|
+
else if (backend === "fastify") frameworkDeps.push("fastify");
|
|
4259
|
+
else if (backend === "self") {
|
|
4260
|
+
if (frontend.includes("next")) frameworkDeps.push("next");
|
|
4261
|
+
}
|
|
4262
|
+
if (frameworkDeps.length > 0) await addPackageDependency({
|
|
4263
|
+
dependencies: frameworkDeps,
|
|
4264
|
+
projectDir: apiPackageDir
|
|
4123
4265
|
});
|
|
4124
4266
|
if (api === "trpc") {
|
|
4125
4267
|
if (backend === "hono") await addPackageDependency({
|
|
4126
4268
|
dependencies: ["@hono/trpc-server"],
|
|
4127
|
-
projectDir:
|
|
4269
|
+
projectDir: apiPackageDir
|
|
4128
4270
|
});
|
|
4129
4271
|
else if (backend === "elysia") await addPackageDependency({
|
|
4130
4272
|
dependencies: ["@elysiajs/trpc"],
|
|
4131
|
-
projectDir:
|
|
4273
|
+
projectDir: apiPackageDir
|
|
4274
|
+
});
|
|
4275
|
+
else if (backend === "express") await addPackageDependency({
|
|
4276
|
+
dependencies: ["@trpc/server"],
|
|
4277
|
+
projectDir: apiPackageDir
|
|
4278
|
+
});
|
|
4279
|
+
else if (backend === "fastify") await addPackageDependency({
|
|
4280
|
+
dependencies: ["@trpc/server"],
|
|
4281
|
+
projectDir: apiPackageDir
|
|
4132
4282
|
});
|
|
4133
4283
|
}
|
|
4284
|
+
if (auth === "better-auth") await addPackageDependency({
|
|
4285
|
+
dependencies: ["better-auth"],
|
|
4286
|
+
projectDir: apiPackageDir
|
|
4287
|
+
});
|
|
4134
4288
|
}
|
|
4135
4289
|
if (webDirExists && apiDeps.web) await addPackageDependency({
|
|
4136
4290
|
dependencies: apiDeps.web.dependencies,
|
|
@@ -4174,7 +4328,7 @@ async function setupApi(config) {
|
|
|
4174
4328
|
//#endregion
|
|
4175
4329
|
//#region src/helpers/core/backend-setup.ts
|
|
4176
4330
|
async function setupBackendDependencies(config) {
|
|
4177
|
-
const { backend, runtime, api, projectDir } = config;
|
|
4331
|
+
const { backend, runtime, api, auth, examples, projectDir } = config;
|
|
4178
4332
|
if (backend === "convex") return;
|
|
4179
4333
|
const framework = backend;
|
|
4180
4334
|
const serverDir = path.join(projectDir, "apps/server");
|
|
@@ -4202,6 +4356,13 @@ async function setupBackendDependencies(config) {
|
|
|
4202
4356
|
dependencies.push("fastify", "@fastify/cors");
|
|
4203
4357
|
if (runtime === "node") devDependencies.push("tsx", "@types/node");
|
|
4204
4358
|
}
|
|
4359
|
+
if (api === "trpc") {
|
|
4360
|
+
if (framework === "express") dependencies.push("@trpc/server");
|
|
4361
|
+
else if (framework === "fastify") dependencies.push("@trpc/server");
|
|
4362
|
+
else if (runtime === "workers") dependencies.push("@trpc/server");
|
|
4363
|
+
} else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
|
|
4364
|
+
if (auth === "better-auth") dependencies.push("better-auth");
|
|
4365
|
+
if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
|
|
4205
4366
|
if (runtime === "bun") devDependencies.push("@types/bun");
|
|
4206
4367
|
if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
|
|
4207
4368
|
dependencies,
|
|
@@ -4220,7 +4381,7 @@ async function setupAuth(config) {
|
|
|
4220
4381
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
4221
4382
|
const clientDirExists = await fs.pathExists(clientDir);
|
|
4222
4383
|
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4223
|
-
|
|
4384
|
+
await fs.pathExists(serverDir);
|
|
4224
4385
|
try {
|
|
4225
4386
|
if (backend === "convex") {
|
|
4226
4387
|
if (auth === "clerk" && clientDirExists) {
|
|
@@ -4276,9 +4437,11 @@ async function setupAuth(config) {
|
|
|
4276
4437
|
});
|
|
4277
4438
|
return;
|
|
4278
4439
|
}
|
|
4279
|
-
|
|
4440
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
4441
|
+
const authPackageDirExists = await fs.pathExists(authPackageDir);
|
|
4442
|
+
if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
|
|
4280
4443
|
dependencies: ["better-auth"],
|
|
4281
|
-
projectDir:
|
|
4444
|
+
projectDir: authPackageDir
|
|
4282
4445
|
});
|
|
4283
4446
|
if (frontend.some((f) => [
|
|
4284
4447
|
"react-router",
|
|
@@ -4300,9 +4463,9 @@ async function setupAuth(config) {
|
|
|
4300
4463
|
dependencies: ["better-auth", "@better-auth/expo"],
|
|
4301
4464
|
projectDir: nativeDir
|
|
4302
4465
|
});
|
|
4303
|
-
if (
|
|
4466
|
+
if (authPackageDirExists) await addPackageDependency({
|
|
4304
4467
|
dependencies: ["@better-auth/expo"],
|
|
4305
|
-
projectDir:
|
|
4468
|
+
projectDir: authPackageDir
|
|
4306
4469
|
});
|
|
4307
4470
|
}
|
|
4308
4471
|
}
|
|
@@ -4394,7 +4557,7 @@ async function setupEnvironmentVariables(config) {
|
|
|
4394
4557
|
const clientVars = [{
|
|
4395
4558
|
key: envVarName,
|
|
4396
4559
|
value: serverUrl,
|
|
4397
|
-
condition:
|
|
4560
|
+
condition: backend !== "self"
|
|
4398
4561
|
}];
|
|
4399
4562
|
if (backend === "convex" && auth === "clerk") {
|
|
4400
4563
|
if (hasNextJs) clientVars.push({
|
|
@@ -4485,8 +4648,6 @@ async function setupEnvironmentVariables(config) {
|
|
|
4485
4648
|
return;
|
|
4486
4649
|
}
|
|
4487
4650
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4488
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
4489
|
-
const envPath = path.join(serverDir, ".env");
|
|
4490
4651
|
let corsOrigin = "http://localhost:3001";
|
|
4491
4652
|
if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
|
|
4492
4653
|
let databaseUrl = null;
|
|
@@ -4502,15 +4663,10 @@ async function setupEnvironmentVariables(config) {
|
|
|
4502
4663
|
break;
|
|
4503
4664
|
case "sqlite":
|
|
4504
4665
|
if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
|
|
4505
|
-
else databaseUrl = "
|
|
4666
|
+
else databaseUrl = `file:${path.join(config.projectDir, "apps/server", "local.db")}`;
|
|
4506
4667
|
break;
|
|
4507
4668
|
}
|
|
4508
4669
|
const serverVars = [
|
|
4509
|
-
{
|
|
4510
|
-
key: "CORS_ORIGIN",
|
|
4511
|
-
value: corsOrigin,
|
|
4512
|
-
condition: true
|
|
4513
|
-
},
|
|
4514
4670
|
{
|
|
4515
4671
|
key: "BETTER_AUTH_SECRET",
|
|
4516
4672
|
value: generateAuthSecret(),
|
|
@@ -4521,16 +4677,6 @@ async function setupEnvironmentVariables(config) {
|
|
|
4521
4677
|
value: "http://localhost:3000",
|
|
4522
4678
|
condition: !!auth
|
|
4523
4679
|
},
|
|
4524
|
-
{
|
|
4525
|
-
key: "DATABASE_URL",
|
|
4526
|
-
value: databaseUrl,
|
|
4527
|
-
condition: database !== "none" && dbSetup === "none"
|
|
4528
|
-
},
|
|
4529
|
-
{
|
|
4530
|
-
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4531
|
-
value: "",
|
|
4532
|
-
condition: examples?.includes("ai") || false
|
|
4533
|
-
},
|
|
4534
4680
|
{
|
|
4535
4681
|
key: "POLAR_ACCESS_TOKEN",
|
|
4536
4682
|
value: "",
|
|
@@ -4540,9 +4686,27 @@ async function setupEnvironmentVariables(config) {
|
|
|
4540
4686
|
key: "POLAR_SUCCESS_URL",
|
|
4541
4687
|
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
|
|
4542
4688
|
condition: config.payments === "polar"
|
|
4689
|
+
},
|
|
4690
|
+
{
|
|
4691
|
+
key: "CORS_ORIGIN",
|
|
4692
|
+
value: corsOrigin,
|
|
4693
|
+
condition: true
|
|
4694
|
+
},
|
|
4695
|
+
{
|
|
4696
|
+
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4697
|
+
value: "",
|
|
4698
|
+
condition: examples?.includes("ai") || false
|
|
4699
|
+
},
|
|
4700
|
+
{
|
|
4701
|
+
key: "DATABASE_URL",
|
|
4702
|
+
value: databaseUrl,
|
|
4703
|
+
condition: database !== "none" && dbSetup === "none"
|
|
4543
4704
|
}
|
|
4544
4705
|
];
|
|
4545
|
-
|
|
4706
|
+
if (backend === "self") {
|
|
4707
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4708
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
|
|
4709
|
+
} else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
|
|
4546
4710
|
const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
|
|
4547
4711
|
const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
|
|
4548
4712
|
if (isUnifiedAlchemy) {
|
|
@@ -4562,12 +4726,15 @@ async function setupEnvironmentVariables(config) {
|
|
|
4562
4726
|
}]);
|
|
4563
4727
|
}
|
|
4564
4728
|
if (serverDeploy === "alchemy") {
|
|
4565
|
-
const
|
|
4566
|
-
if (await fs.pathExists(serverDir$1)) await addEnvVariablesToFile(path.join(serverDir$1, ".env"), [{
|
|
4729
|
+
const serverAlchemyVars = [{
|
|
4567
4730
|
key: "ALCHEMY_PASSWORD",
|
|
4568
4731
|
value: "please-change-this",
|
|
4569
4732
|
condition: true
|
|
4570
|
-
}]
|
|
4733
|
+
}];
|
|
4734
|
+
if (backend === "self") {
|
|
4735
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4736
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
|
|
4737
|
+
} else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
|
|
4571
4738
|
}
|
|
4572
4739
|
}
|
|
4573
4740
|
}
|
|
@@ -4603,7 +4770,7 @@ async function setupCloudflareD1(config) {
|
|
|
4603
4770
|
const envPath = path.join(projectDir, "apps/server", ".env");
|
|
4604
4771
|
const variables = [{
|
|
4605
4772
|
key: "DATABASE_URL",
|
|
4606
|
-
value: "
|
|
4773
|
+
value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
|
|
4607
4774
|
condition: true
|
|
4608
4775
|
}];
|
|
4609
4776
|
try {
|
|
@@ -5133,9 +5300,9 @@ async function writeEnvFile$1(projectDir, config) {
|
|
|
5133
5300
|
}
|
|
5134
5301
|
async function addDotenvImportToPrismaConfig(projectDir) {
|
|
5135
5302
|
try {
|
|
5136
|
-
const prismaConfigPath = path.join(projectDir, "
|
|
5303
|
+
const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
|
|
5137
5304
|
let content = await fs.readFile(prismaConfigPath, "utf8");
|
|
5138
|
-
content = `import "dotenv
|
|
5305
|
+
content = `import dotenv from "dotenv";\ndotenv.config({ path: "../../apps/server/.env" });\n${content}`;
|
|
5139
5306
|
await fs.writeFile(prismaConfigPath, content);
|
|
5140
5307
|
} catch (_error) {
|
|
5141
5308
|
consola$1.error("Failed to update prisma.config.ts");
|
|
@@ -5151,11 +5318,12 @@ function displayManualSetupInstructions$1() {
|
|
|
5151
5318
|
|
|
5152
5319
|
DATABASE_URL="your_database_url"`);
|
|
5153
5320
|
}
|
|
5154
|
-
async function addPrismaAccelerateExtension(
|
|
5321
|
+
async function addPrismaAccelerateExtension(projectDir) {
|
|
5155
5322
|
try {
|
|
5323
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5156
5324
|
await addPackageDependency({
|
|
5157
5325
|
dependencies: ["@prisma/extension-accelerate"],
|
|
5158
|
-
projectDir:
|
|
5326
|
+
projectDir: dbPackageDir
|
|
5159
5327
|
});
|
|
5160
5328
|
return true;
|
|
5161
5329
|
} catch (_error) {
|
|
@@ -5216,7 +5384,7 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5216
5384
|
await writeEnvFile$1(projectDir, prismaConfig);
|
|
5217
5385
|
if (orm === "prisma") {
|
|
5218
5386
|
await addDotenvImportToPrismaConfig(projectDir);
|
|
5219
|
-
await addPrismaAccelerateExtension(
|
|
5387
|
+
await addPrismaAccelerateExtension(projectDir);
|
|
5220
5388
|
}
|
|
5221
5389
|
const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
|
|
5222
5390
|
log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
|
|
@@ -5316,18 +5484,18 @@ function displayManualSupabaseInstructions(output) {
|
|
|
5316
5484
|
log.info(`"Manual Supabase Setup Instructions:"
|
|
5317
5485
|
1. Ensure Docker is installed and running.
|
|
5318
5486
|
2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
|
|
5319
|
-
3. Run \`supabase init\` in your project's \`
|
|
5320
|
-
4. Run \`supabase start\` in your project's \`
|
|
5487
|
+
3. Run \`supabase init\` in your project's \`packages/db\` directory.
|
|
5488
|
+
4. Run \`supabase start\` in your project's \`packages/db\` directory.
|
|
5321
5489
|
5. Copy the 'DB URL' from the output.${output ? `
|
|
5322
5490
|
${pc.bold("Relevant output from `supabase start`:")}
|
|
5323
5491
|
${pc.dim(output)}` : ""}
|
|
5324
|
-
6. Add the DB URL to the .env file in \`
|
|
5492
|
+
6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
|
|
5325
5493
|
${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
|
|
5326
5494
|
}
|
|
5327
5495
|
async function setupSupabase(config, cliInput) {
|
|
5328
5496
|
const { projectDir, packageManager } = config;
|
|
5329
5497
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5330
|
-
const serverDir = path.join(projectDir, "
|
|
5498
|
+
const serverDir = path.join(projectDir, "packages", "db");
|
|
5331
5499
|
try {
|
|
5332
5500
|
await fs.ensureDir(serverDir);
|
|
5333
5501
|
if (manualDb) {
|
|
@@ -5610,15 +5778,15 @@ async function setupDatabase(config, cliInput) {
|
|
|
5610
5778
|
const { database, orm, dbSetup, backend, projectDir } = config;
|
|
5611
5779
|
if (backend === "convex" || database === "none") {
|
|
5612
5780
|
if (backend !== "convex") {
|
|
5613
|
-
const serverDir
|
|
5614
|
-
const serverDbDir = path.join(serverDir
|
|
5781
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
5782
|
+
const serverDbDir = path.join(serverDir, "src/db");
|
|
5615
5783
|
if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
|
|
5616
5784
|
}
|
|
5617
5785
|
return;
|
|
5618
5786
|
}
|
|
5619
5787
|
const s = spinner();
|
|
5620
|
-
const
|
|
5621
|
-
if (!await fs.pathExists(
|
|
5788
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5789
|
+
if (!await fs.pathExists(dbPackageDir)) return;
|
|
5622
5790
|
try {
|
|
5623
5791
|
if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
|
|
5624
5792
|
dependencies: [
|
|
@@ -5627,23 +5795,27 @@ async function setupDatabase(config, cliInput) {
|
|
|
5627
5795
|
"@planetscale/database"
|
|
5628
5796
|
],
|
|
5629
5797
|
devDependencies: ["prisma"],
|
|
5630
|
-
projectDir:
|
|
5798
|
+
projectDir: dbPackageDir
|
|
5631
5799
|
});
|
|
5632
5800
|
else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
|
|
5633
5801
|
dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
|
|
5634
5802
|
devDependencies: ["prisma"],
|
|
5635
|
-
projectDir:
|
|
5803
|
+
projectDir: dbPackageDir
|
|
5636
5804
|
});
|
|
5637
5805
|
else await addPackageDependency({
|
|
5638
5806
|
dependencies: ["@prisma/client"],
|
|
5639
5807
|
devDependencies: ["prisma"],
|
|
5640
|
-
projectDir:
|
|
5808
|
+
projectDir: dbPackageDir
|
|
5641
5809
|
});
|
|
5642
5810
|
else if (orm === "drizzle") {
|
|
5643
5811
|
if (database === "sqlite") await addPackageDependency({
|
|
5644
|
-
dependencies: [
|
|
5812
|
+
dependencies: [
|
|
5813
|
+
"drizzle-orm",
|
|
5814
|
+
"@libsql/client",
|
|
5815
|
+
"libsql"
|
|
5816
|
+
],
|
|
5645
5817
|
devDependencies: ["drizzle-kit"],
|
|
5646
|
-
projectDir:
|
|
5818
|
+
projectDir: dbPackageDir
|
|
5647
5819
|
});
|
|
5648
5820
|
else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
|
|
5649
5821
|
dependencies: [
|
|
@@ -5652,32 +5824,32 @@ async function setupDatabase(config, cliInput) {
|
|
|
5652
5824
|
"ws"
|
|
5653
5825
|
],
|
|
5654
5826
|
devDependencies: ["drizzle-kit", "@types/ws"],
|
|
5655
|
-
projectDir:
|
|
5827
|
+
projectDir: dbPackageDir
|
|
5656
5828
|
});
|
|
5657
5829
|
else if (dbSetup === "planetscale") await addPackageDependency({
|
|
5658
5830
|
dependencies: ["drizzle-orm", "pg"],
|
|
5659
5831
|
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5660
|
-
projectDir:
|
|
5832
|
+
projectDir: dbPackageDir
|
|
5661
5833
|
});
|
|
5662
5834
|
else await addPackageDependency({
|
|
5663
5835
|
dependencies: ["drizzle-orm", "pg"],
|
|
5664
5836
|
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5665
|
-
projectDir:
|
|
5837
|
+
projectDir: dbPackageDir
|
|
5666
5838
|
});
|
|
5667
5839
|
else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
|
|
5668
5840
|
dependencies: ["drizzle-orm", "@planetscale/database"],
|
|
5669
5841
|
devDependencies: ["drizzle-kit"],
|
|
5670
|
-
projectDir:
|
|
5842
|
+
projectDir: dbPackageDir
|
|
5671
5843
|
});
|
|
5672
5844
|
else await addPackageDependency({
|
|
5673
5845
|
dependencies: ["drizzle-orm", "mysql2"],
|
|
5674
5846
|
devDependencies: ["drizzle-kit"],
|
|
5675
|
-
projectDir:
|
|
5847
|
+
projectDir: dbPackageDir
|
|
5676
5848
|
});
|
|
5677
5849
|
} else if (orm === "mongoose") await addPackageDependency({
|
|
5678
5850
|
dependencies: ["mongoose"],
|
|
5679
5851
|
devDependencies: [],
|
|
5680
|
-
projectDir:
|
|
5852
|
+
projectDir: dbPackageDir
|
|
5681
5853
|
});
|
|
5682
5854
|
if (dbSetup === "docker") await setupDockerCompose(config);
|
|
5683
5855
|
else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
|
|
@@ -5700,7 +5872,7 @@ async function setupDatabase(config, cliInput) {
|
|
|
5700
5872
|
//#region src/helpers/core/runtime-setup.ts
|
|
5701
5873
|
async function setupRuntime(config) {
|
|
5702
5874
|
const { runtime, backend, projectDir } = config;
|
|
5703
|
-
if (backend === "convex" || backend === "
|
|
5875
|
+
if (backend === "convex" || backend === "self" || runtime === "none") return;
|
|
5704
5876
|
const serverDir = path.join(projectDir, "apps/server");
|
|
5705
5877
|
if (!await fs.pathExists(serverDir)) return;
|
|
5706
5878
|
if (runtime === "bun") await setupBunRuntime(serverDir, backend);
|
|
@@ -6220,7 +6392,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6220
6392
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
6221
6393
|
if (!isConvex) {
|
|
6222
6394
|
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6223
|
-
if (api === "orpc") if (backend === "
|
|
6395
|
+
if (api === "orpc") if (backend === "self") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
|
|
6224
6396
|
else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6225
6397
|
}
|
|
6226
6398
|
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
@@ -6334,12 +6506,79 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
|
6334
6506
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6335
6507
|
}
|
|
6336
6508
|
|
|
6509
|
+
//#endregion
|
|
6510
|
+
//#region src/helpers/core/workspace-setup.ts
|
|
6511
|
+
async function setupWorkspaceDependencies(projectDir, options) {
|
|
6512
|
+
const projectName = options.projectName;
|
|
6513
|
+
const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
|
|
6514
|
+
const commonDeps = ["dotenv", "zod"];
|
|
6515
|
+
const commonDevDeps = ["tsdown"];
|
|
6516
|
+
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
6517
|
+
if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
|
|
6518
|
+
dependencies: commonDeps,
|
|
6519
|
+
devDependencies: commonDevDeps,
|
|
6520
|
+
projectDir: dbPackageDir
|
|
6521
|
+
});
|
|
6522
|
+
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
6523
|
+
if (await fs.pathExists(authPackageDir)) await addPackageDependency({
|
|
6524
|
+
dependencies: commonDeps,
|
|
6525
|
+
devDependencies: commonDevDeps,
|
|
6526
|
+
customDependencies: { [`@${projectName}/db`]: workspaceVersion },
|
|
6527
|
+
projectDir: authPackageDir
|
|
6528
|
+
});
|
|
6529
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
6530
|
+
if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
|
|
6531
|
+
dependencies: commonDeps,
|
|
6532
|
+
devDependencies: commonDevDeps,
|
|
6533
|
+
customDependencies: {
|
|
6534
|
+
[`@${projectName}/auth`]: workspaceVersion,
|
|
6535
|
+
[`@${projectName}/db`]: workspaceVersion
|
|
6536
|
+
},
|
|
6537
|
+
projectDir: apiPackageDir
|
|
6538
|
+
});
|
|
6539
|
+
const serverPackageDir = path.join(projectDir, "apps/server");
|
|
6540
|
+
if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
|
|
6541
|
+
dependencies: commonDeps,
|
|
6542
|
+
devDependencies: commonDevDeps,
|
|
6543
|
+
customDependencies: {
|
|
6544
|
+
[`@${projectName}/api`]: workspaceVersion,
|
|
6545
|
+
[`@${projectName}/auth`]: workspaceVersion,
|
|
6546
|
+
[`@${projectName}/db`]: workspaceVersion
|
|
6547
|
+
},
|
|
6548
|
+
projectDir: serverPackageDir
|
|
6549
|
+
});
|
|
6550
|
+
const needsApiDependency = options.api && options.api !== "none";
|
|
6551
|
+
const webPackageDir = path.join(projectDir, "apps/web");
|
|
6552
|
+
if (await fs.pathExists(webPackageDir)) {
|
|
6553
|
+
const webDeps = {};
|
|
6554
|
+
if (options.backend === "self") {
|
|
6555
|
+
webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6556
|
+
webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6557
|
+
webDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6558
|
+
} else if (needsApiDependency) webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6559
|
+
if (Object.keys(webDeps).length > 0) await addPackageDependency({
|
|
6560
|
+
customDependencies: webDeps,
|
|
6561
|
+
projectDir: webPackageDir
|
|
6562
|
+
});
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6337
6566
|
//#endregion
|
|
6338
6567
|
//#region src/helpers/core/project-config.ts
|
|
6339
6568
|
async function updatePackageConfigurations(projectDir, options) {
|
|
6340
6569
|
await updateRootPackageJson(projectDir, options);
|
|
6341
|
-
if (options.backend
|
|
6342
|
-
else
|
|
6570
|
+
if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
|
|
6571
|
+
else if (options.backend === "self") {
|
|
6572
|
+
await updateDbPackageJson(projectDir, options);
|
|
6573
|
+
await updateAuthPackageJson(projectDir, options);
|
|
6574
|
+
await updateApiPackageJson(projectDir, options);
|
|
6575
|
+
await setupWorkspaceDependencies(projectDir, options);
|
|
6576
|
+
} else if (options.backend !== "none") {
|
|
6577
|
+
await updateServerPackageJson(projectDir, options);
|
|
6578
|
+
await updateAuthPackageJson(projectDir, options);
|
|
6579
|
+
await updateApiPackageJson(projectDir, options);
|
|
6580
|
+
await setupWorkspaceDependencies(projectDir, options);
|
|
6581
|
+
}
|
|
6343
6582
|
}
|
|
6344
6583
|
async function updateRootPackageJson(projectDir, options) {
|
|
6345
6584
|
const rootPackageJsonPath = path.join(projectDir, "package.json");
|
|
@@ -6349,6 +6588,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6349
6588
|
if (!packageJson.scripts) packageJson.scripts = {};
|
|
6350
6589
|
const scripts = packageJson.scripts;
|
|
6351
6590
|
const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
|
|
6591
|
+
const dbPackageName = `@${options.projectName}/db`;
|
|
6352
6592
|
let serverDevScript = "";
|
|
6353
6593
|
if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
|
|
6354
6594
|
else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
|
|
@@ -6365,17 +6605,17 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6365
6605
|
scripts["check-types"] = "turbo check-types";
|
|
6366
6606
|
scripts["dev:native"] = "turbo -F native dev";
|
|
6367
6607
|
scripts["dev:web"] = "turbo -F web dev";
|
|
6368
|
-
scripts["dev:server"] = serverDevScript;
|
|
6608
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6369
6609
|
if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
|
|
6370
6610
|
if (needsDbScripts) {
|
|
6371
|
-
scripts["db:push"] = `turbo -F ${
|
|
6372
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${
|
|
6611
|
+
scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
|
|
6612
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
|
|
6373
6613
|
if (options.orm === "prisma") {
|
|
6374
|
-
scripts["db:generate"] = `turbo -F ${
|
|
6375
|
-
scripts["db:migrate"] = `turbo -F ${
|
|
6614
|
+
scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
|
|
6615
|
+
scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
|
|
6376
6616
|
} else if (options.orm === "drizzle") {
|
|
6377
|
-
scripts["db:generate"] = `turbo -F ${
|
|
6378
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${
|
|
6617
|
+
scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
|
|
6618
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
|
|
6379
6619
|
}
|
|
6380
6620
|
}
|
|
6381
6621
|
if (options.dbSetup === "docker") {
|
|
@@ -6390,17 +6630,17 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6390
6630
|
scripts["check-types"] = "pnpm -r check-types";
|
|
6391
6631
|
scripts["dev:native"] = "pnpm --filter native dev";
|
|
6392
6632
|
scripts["dev:web"] = "pnpm --filter web dev";
|
|
6393
|
-
scripts["dev:server"] = serverDevScript;
|
|
6633
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6394
6634
|
if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
|
|
6395
6635
|
if (needsDbScripts) {
|
|
6396
|
-
scripts["db:push"] = `pnpm --filter ${
|
|
6397
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${
|
|
6636
|
+
scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
|
|
6637
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
|
|
6398
6638
|
if (options.orm === "prisma") {
|
|
6399
|
-
scripts["db:generate"] = `pnpm --filter ${
|
|
6400
|
-
scripts["db:migrate"] = `pnpm --filter ${
|
|
6639
|
+
scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
|
|
6640
|
+
scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
|
|
6401
6641
|
} else if (options.orm === "drizzle") {
|
|
6402
|
-
scripts["db:generate"] = `pnpm --filter ${
|
|
6403
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${
|
|
6642
|
+
scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
|
|
6643
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
|
|
6404
6644
|
}
|
|
6405
6645
|
}
|
|
6406
6646
|
if (options.dbSetup === "docker") {
|
|
@@ -6415,17 +6655,17 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6415
6655
|
scripts["check-types"] = "npm run check-types --workspaces";
|
|
6416
6656
|
scripts["dev:native"] = "npm run dev --workspace native";
|
|
6417
6657
|
scripts["dev:web"] = "npm run dev --workspace web";
|
|
6418
|
-
scripts["dev:server"] = serverDevScript;
|
|
6658
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6419
6659
|
if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
|
|
6420
6660
|
if (needsDbScripts) {
|
|
6421
|
-
scripts["db:push"] = `npm run db:push --workspace ${
|
|
6422
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${
|
|
6661
|
+
scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
|
|
6662
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
|
|
6423
6663
|
if (options.orm === "prisma") {
|
|
6424
|
-
scripts["db:generate"] = `npm run db:generate --workspace ${
|
|
6425
|
-
scripts["db:migrate"] = `npm run db:migrate --workspace ${
|
|
6664
|
+
scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
|
|
6665
|
+
scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
|
|
6426
6666
|
} else if (options.orm === "drizzle") {
|
|
6427
|
-
scripts["db:generate"] = `npm run db:generate --workspace ${
|
|
6428
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${
|
|
6667
|
+
scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
|
|
6668
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
|
|
6429
6669
|
}
|
|
6430
6670
|
}
|
|
6431
6671
|
if (options.dbSetup === "docker") {
|
|
@@ -6440,17 +6680,17 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6440
6680
|
scripts["check-types"] = "bun run --filter '*' check-types";
|
|
6441
6681
|
scripts["dev:native"] = "bun run --filter native dev";
|
|
6442
6682
|
scripts["dev:web"] = "bun run --filter web dev";
|
|
6443
|
-
scripts["dev:server"] = serverDevScript;
|
|
6683
|
+
if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
|
|
6444
6684
|
if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
|
|
6445
6685
|
if (needsDbScripts) {
|
|
6446
|
-
scripts["db:push"] = `bun run --filter ${
|
|
6447
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${
|
|
6686
|
+
scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
|
|
6687
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
|
|
6448
6688
|
if (options.orm === "prisma") {
|
|
6449
|
-
scripts["db:generate"] = `bun run --filter ${
|
|
6450
|
-
scripts["db:migrate"] = `bun run --filter ${
|
|
6689
|
+
scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
|
|
6690
|
+
scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
|
|
6451
6691
|
} else if (options.orm === "drizzle") {
|
|
6452
|
-
scripts["db:generate"] = `bun run --filter ${
|
|
6453
|
-
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${
|
|
6692
|
+
scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
|
|
6693
|
+
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
|
|
6454
6694
|
}
|
|
6455
6695
|
}
|
|
6456
6696
|
if (options.dbSetup === "docker") {
|
|
@@ -6483,6 +6723,22 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6483
6723
|
const serverPackageJson = await fs.readJson(serverPackageJsonPath);
|
|
6484
6724
|
if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
|
|
6485
6725
|
const scripts = serverPackageJson.scripts;
|
|
6726
|
+
if (options.dbSetup === "docker") {
|
|
6727
|
+
scripts["db:start"] = "docker compose up -d";
|
|
6728
|
+
scripts["db:watch"] = "docker compose up";
|
|
6729
|
+
scripts["db:stop"] = "docker compose stop";
|
|
6730
|
+
scripts["db:down"] = "docker compose down";
|
|
6731
|
+
}
|
|
6732
|
+
await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
|
|
6733
|
+
await updateDbPackageJson(projectDir, options);
|
|
6734
|
+
}
|
|
6735
|
+
async function updateDbPackageJson(projectDir, options) {
|
|
6736
|
+
const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
|
|
6737
|
+
if (!await fs.pathExists(dbPackageJsonPath)) return;
|
|
6738
|
+
const dbPackageJson = await fs.readJson(dbPackageJsonPath);
|
|
6739
|
+
dbPackageJson.name = `@${options.projectName}/db`;
|
|
6740
|
+
if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
|
|
6741
|
+
const scripts = dbPackageJson.scripts;
|
|
6486
6742
|
if (options.database !== "none") {
|
|
6487
6743
|
if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
|
|
6488
6744
|
if (options.orm === "prisma") {
|
|
@@ -6497,13 +6753,21 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6497
6753
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "drizzle-kit migrate";
|
|
6498
6754
|
}
|
|
6499
6755
|
}
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6756
|
+
await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
|
|
6757
|
+
}
|
|
6758
|
+
async function updateAuthPackageJson(projectDir, options) {
|
|
6759
|
+
const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
|
|
6760
|
+
if (!await fs.pathExists(authPackageJsonPath)) return;
|
|
6761
|
+
const authPackageJson = await fs.readJson(authPackageJsonPath);
|
|
6762
|
+
authPackageJson.name = `@${options.projectName}/auth`;
|
|
6763
|
+
await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
|
|
6764
|
+
}
|
|
6765
|
+
async function updateApiPackageJson(projectDir, options) {
|
|
6766
|
+
const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
|
|
6767
|
+
if (!await fs.pathExists(apiPackageJsonPath)) return;
|
|
6768
|
+
const apiPackageJson = await fs.readJson(apiPackageJsonPath);
|
|
6769
|
+
apiPackageJson.name = `@${options.projectName}/api`;
|
|
6770
|
+
await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
|
|
6507
6771
|
}
|
|
6508
6772
|
async function updateConvexPackageJson(projectDir, options) {
|
|
6509
6773
|
const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
|
|
@@ -6519,15 +6783,14 @@ async function updateConvexPackageJson(projectDir, options) {
|
|
|
6519
6783
|
async function createProject(options, cliInput) {
|
|
6520
6784
|
const projectDir = options.projectDir;
|
|
6521
6785
|
const isConvex = options.backend === "convex";
|
|
6786
|
+
const isSelfBackend = options.backend === "self";
|
|
6787
|
+
const needsServerSetup = !isConvex && !isSelfBackend;
|
|
6522
6788
|
try {
|
|
6523
6789
|
await fs.ensureDir(projectDir);
|
|
6524
6790
|
await copyBaseTemplate(projectDir, options);
|
|
6525
6791
|
await setupFrontendTemplates(projectDir, options);
|
|
6526
6792
|
await setupBackendFramework(projectDir, options);
|
|
6527
|
-
if (
|
|
6528
|
-
await setupDbOrmTemplates(projectDir, options);
|
|
6529
|
-
await setupDockerComposeTemplates(projectDir, options);
|
|
6530
|
-
}
|
|
6793
|
+
if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
|
|
6531
6794
|
await setupAuthTemplate(projectDir, options);
|
|
6532
6795
|
if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
|
|
6533
6796
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
@@ -6535,9 +6798,11 @@ async function createProject(options, cliInput) {
|
|
|
6535
6798
|
await setupDeploymentTemplates(projectDir, options);
|
|
6536
6799
|
await setupApi(options);
|
|
6537
6800
|
if (!isConvex) {
|
|
6538
|
-
|
|
6801
|
+
if (needsServerSetup) {
|
|
6802
|
+
await setupBackendDependencies(options);
|
|
6803
|
+
await setupRuntime(options);
|
|
6804
|
+
}
|
|
6539
6805
|
await setupDatabase(options, cliInput);
|
|
6540
|
-
await setupRuntime(options);
|
|
6541
6806
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
|
|
6542
6807
|
}
|
|
6543
6808
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
@@ -6546,6 +6811,7 @@ async function createProject(options, cliInput) {
|
|
|
6546
6811
|
await handleExtras(projectDir, options);
|
|
6547
6812
|
await setupEnvironmentVariables(options);
|
|
6548
6813
|
await updatePackageConfigurations(projectDir, options);
|
|
6814
|
+
await setupCatalogs(projectDir, options);
|
|
6549
6815
|
await setupWebDeploy(options);
|
|
6550
6816
|
await setupServerDeploy(options);
|
|
6551
6817
|
await createReadme(projectDir, options);
|