create-better-t-stack 2.50.0-canary.dd7000f2 → 2.50.1-canary.58bbe5f6
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 +6 -7
- package/dist/index.js +1 -1
- package/dist/{src-dv6H37db.js → src-CulfT5QB.js} +461 -358
- package/package.json +1 -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/server/package.json.hbs +1 -3
- package/templates/api/orpc/server/tsconfig.json.hbs +8 -8
- 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/package.json.hbs +1 -3
- package/templates/api/trpc/server/tsconfig.json.hbs +8 -11
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
- package/templates/auth/better-auth/fullstack/next/src/app/api/auth/[...all]/route.ts.hbs +4 -0
- package/templates/auth/better-auth/server/base/package.json.hbs +1 -4
- package/templates/auth/better-auth/server/base/src/index.ts.hbs +5 -5
- package/templates/auth/better-auth/server/base/tsconfig.json.hbs +8 -11
- 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 +31 -0
- package/templates/backend/server/base/package.json.hbs +1 -4
- package/templates/backend/server/base/tsconfig.json.hbs +1 -15
- package/templates/backend/server/base/tsdown.config.ts.hbs +1 -6
- package/templates/base/tsconfig.base.json.hbs +34 -0
- package/templates/base/tsconfig.json.hbs +3 -0
- package/templates/db/base/package.json.hbs +1 -3
- package/templates/db/base/tsconfig.json.hbs +8 -11
- package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +5 -1
- package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +5 -1
- package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +5 -1
- package/templates/db/prisma/mongodb/prisma.config.ts.hbs +5 -1
- package/templates/db/prisma/mysql/prisma.config.ts.hbs +5 -1
- package/templates/db/prisma/postgres/prisma.config.ts.hbs +5 -1
- package/templates/db/prisma/sqlite/prisma.config.ts.hbs +5 -1
- 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 +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/base/tsconfig.base.json +0 -23
|
@@ -71,8 +71,7 @@ const dependencyVersionMap = {
|
|
|
71
71
|
"drizzle-orm": "^0.44.2",
|
|
72
72
|
"drizzle-kit": "^0.31.2",
|
|
73
73
|
"@planetscale/database": "^1.19.0",
|
|
74
|
-
"@libsql/client": "^0.
|
|
75
|
-
libsql: "^0.5.22",
|
|
74
|
+
"@libsql/client": "^0.14.0",
|
|
76
75
|
"@neondatabase/serverless": "^1.0.1",
|
|
77
76
|
pg: "^8.14.1",
|
|
78
77
|
"@types/pg": "^8.11.11",
|
|
@@ -125,7 +124,7 @@ const dependencyVersionMap = {
|
|
|
125
124
|
"@trpc/tanstack-react-query": "^11.5.0",
|
|
126
125
|
"@trpc/server": "^11.5.0",
|
|
127
126
|
"@trpc/client": "^11.5.0",
|
|
128
|
-
next: "
|
|
127
|
+
next: "15.5.4",
|
|
129
128
|
convex: "^1.27.0",
|
|
130
129
|
"@convex-dev/react-query": "^0.0.0-alpha.8",
|
|
131
130
|
"convex-svelte": "^0.0.11",
|
|
@@ -348,21 +347,16 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
348
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");
|
|
349
348
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
350
349
|
}
|
|
351
|
-
const FULLSTACK_FRONTENDS$1 = [
|
|
352
|
-
"next",
|
|
353
|
-
"nuxt",
|
|
354
|
-
"svelte",
|
|
355
|
-
"tanstack-start"
|
|
356
|
-
];
|
|
350
|
+
const FULLSTACK_FRONTENDS$1 = ["next"];
|
|
357
351
|
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
358
352
|
const backend = config.backend || options.backend;
|
|
359
353
|
const frontends = config.frontend || options.frontend || [];
|
|
360
354
|
if (backend === "self") {
|
|
361
|
-
if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack)
|
|
355
|
+
if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) 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.");
|
|
362
356
|
if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
|
|
363
357
|
}
|
|
364
358
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
365
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack)
|
|
359
|
+
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.");
|
|
366
360
|
}
|
|
367
361
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
368
362
|
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.`);
|
|
@@ -685,12 +679,7 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
|
685
679
|
|
|
686
680
|
//#endregion
|
|
687
681
|
//#region src/prompts/backend.ts
|
|
688
|
-
const FULLSTACK_FRONTENDS = [
|
|
689
|
-
"next",
|
|
690
|
-
"nuxt",
|
|
691
|
-
"svelte",
|
|
692
|
-
"tanstack-start"
|
|
693
|
-
];
|
|
682
|
+
const FULLSTACK_FRONTENDS = ["next"];
|
|
694
683
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
695
684
|
if (backendFramework !== void 0) return backendFramework;
|
|
696
685
|
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
|
|
@@ -699,7 +688,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
699
688
|
if (hasFullstackFrontend) backendOptions.push({
|
|
700
689
|
value: "self",
|
|
701
690
|
label: "Self (Fullstack)",
|
|
702
|
-
hint: "Use frontend's built-in
|
|
691
|
+
hint: "Use frontend's built-in api routes"
|
|
703
692
|
});
|
|
704
693
|
backendOptions.push({
|
|
705
694
|
value: "hono",
|
|
@@ -2089,11 +2078,14 @@ async function setupFumadocs(config) {
|
|
|
2089
2078
|
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
2090
2079
|
const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
|
|
2091
2080
|
const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
2081
|
+
const s = spinner();
|
|
2082
|
+
s.start("Setting up Fumadocs...");
|
|
2092
2083
|
await execa(fumadocsInitCommand, {
|
|
2093
2084
|
cwd: path.join(projectDir, "apps"),
|
|
2094
2085
|
env: { CI: "true" },
|
|
2095
2086
|
shell: true
|
|
2096
2087
|
});
|
|
2088
|
+
s.stop("Fumadocs setup complete!");
|
|
2097
2089
|
const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
|
|
2098
2090
|
const packageJsonPath = path.join(fumadocsDir, "package.json");
|
|
2099
2091
|
if (await fs.pathExists(packageJsonPath)) {
|
|
@@ -2334,11 +2326,14 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2334
2326
|
if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
|
|
2335
2327
|
const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
|
|
2336
2328
|
const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
2329
|
+
const s = spinner();
|
|
2330
|
+
s.start("Setting up Ultracite...");
|
|
2337
2331
|
await execa(ultraciteInitCommand, {
|
|
2338
2332
|
cwd: projectDir,
|
|
2339
2333
|
env: { CI: "true" },
|
|
2340
2334
|
shell: true
|
|
2341
2335
|
});
|
|
2336
|
+
s.stop("Ultracite setup complete!");
|
|
2342
2337
|
if (hasHusky) await addPackageDependency({
|
|
2343
2338
|
devDependencies: ["husky", "lint-staged"],
|
|
2344
2339
|
projectDir
|
|
@@ -2749,6 +2744,10 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2749
2744
|
const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
|
|
2750
2745
|
if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
|
|
2751
2746
|
}
|
|
2747
|
+
if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
|
|
2748
|
+
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
|
|
2749
|
+
if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
|
|
2750
|
+
}
|
|
2752
2751
|
}
|
|
2753
2752
|
} else if (hasNuxtWeb) {
|
|
2754
2753
|
const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
|
|
@@ -2944,6 +2943,10 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2944
2943
|
if (reactFramework) {
|
|
2945
2944
|
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2946
2945
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2946
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
2947
|
+
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
|
|
2948
|
+
if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
|
|
2949
|
+
}
|
|
2947
2950
|
}
|
|
2948
2951
|
} else if (hasNuxtWeb) {
|
|
2949
2952
|
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
@@ -3086,6 +3089,10 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3086
3089
|
if (reactFramework) {
|
|
3087
3090
|
const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
|
|
3088
3091
|
if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
|
|
3092
|
+
if (context.backend === "self" && reactFramework === "next") {
|
|
3093
|
+
const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
|
|
3094
|
+
if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
|
|
3095
|
+
}
|
|
3089
3096
|
}
|
|
3090
3097
|
}
|
|
3091
3098
|
} else if (hasNuxtWeb) {
|
|
@@ -3138,25 +3145,29 @@ async function setupDockerComposeTemplates(projectDir, context) {
|
|
|
3138
3145
|
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
|
|
3139
3146
|
}
|
|
3140
3147
|
async function setupDeploymentTemplates(projectDir, context) {
|
|
3141
|
-
|
|
3148
|
+
const isBackendSelf = context.backend === "self";
|
|
3149
|
+
if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
|
|
3142
3150
|
const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
|
|
3143
|
-
if (
|
|
3144
|
-
await
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
await
|
|
3159
|
-
|
|
3151
|
+
if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
|
|
3152
|
+
if (await fs.pathExists(alchemyTemplateSrc)) {
|
|
3153
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
|
|
3154
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3155
|
+
}
|
|
3156
|
+
} else {
|
|
3157
|
+
if (context.webDeploy === "alchemy") {
|
|
3158
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
3159
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
|
|
3160
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
|
|
3161
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
if (context.serverDeploy === "alchemy" && !isBackendSelf) {
|
|
3165
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3166
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
|
|
3167
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3168
|
+
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3169
|
+
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3170
|
+
}
|
|
3160
3171
|
}
|
|
3161
3172
|
}
|
|
3162
3173
|
}
|
|
@@ -3179,7 +3190,7 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3179
3190
|
}
|
|
3180
3191
|
}
|
|
3181
3192
|
}
|
|
3182
|
-
if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
|
|
3193
|
+
if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
|
|
3183
3194
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3184
3195
|
if (await fs.pathExists(serverAppDir)) {
|
|
3185
3196
|
const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
|
|
@@ -3187,6 +3198,18 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3187
3198
|
}
|
|
3188
3199
|
}
|
|
3189
3200
|
}
|
|
3201
|
+
async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
|
|
3202
|
+
for (const packageName of [
|
|
3203
|
+
"packages/api",
|
|
3204
|
+
"packages/auth",
|
|
3205
|
+
"packages/db"
|
|
3206
|
+
]) {
|
|
3207
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3208
|
+
if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
|
|
3209
|
+
}
|
|
3210
|
+
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3211
|
+
if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
3212
|
+
}
|
|
3190
3213
|
|
|
3191
3214
|
//#endregion
|
|
3192
3215
|
//#region src/helpers/core/add-addons.ts
|
|
@@ -3252,7 +3275,7 @@ async function setupServerDeploy(config) {
|
|
|
3252
3275
|
serverDir,
|
|
3253
3276
|
packageManager
|
|
3254
3277
|
});
|
|
3255
|
-
} else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
|
|
3278
|
+
} else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
|
|
3256
3279
|
}
|
|
3257
3280
|
async function setupWorkersServerDeploy(serverDir, _packageManager) {
|
|
3258
3281
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
@@ -3289,18 +3312,18 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
|
|
|
3289
3312
|
log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
|
|
3290
3313
|
}
|
|
3291
3314
|
}
|
|
3292
|
-
async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
3315
|
+
async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
|
|
3293
3316
|
if (!await fs.pathExists(serverDir)) return;
|
|
3294
3317
|
await addPackageDependency({
|
|
3295
3318
|
devDependencies: [
|
|
3296
3319
|
"alchemy",
|
|
3297
3320
|
"wrangler",
|
|
3298
3321
|
"@types/node",
|
|
3299
|
-
"dotenv",
|
|
3300
3322
|
"@cloudflare/workers-types"
|
|
3301
3323
|
],
|
|
3302
3324
|
projectDir: serverDir
|
|
3303
3325
|
});
|
|
3326
|
+
if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
|
|
3304
3327
|
const packageJsonPath = path.join(serverDir, "package.json");
|
|
3305
3328
|
if (await fs.pathExists(packageJsonPath)) {
|
|
3306
3329
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
@@ -3313,6 +3336,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
|
3313
3336
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3314
3337
|
}
|
|
3315
3338
|
}
|
|
3339
|
+
async function addAlchemyPackagesDependencies$1(projectDir) {
|
|
3340
|
+
for (const packageName of [
|
|
3341
|
+
"packages/api",
|
|
3342
|
+
"packages/auth",
|
|
3343
|
+
"packages/db"
|
|
3344
|
+
]) {
|
|
3345
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3346
|
+
if (await fs.pathExists(packageDir)) await addPackageDependency({
|
|
3347
|
+
devDependencies: ["@cloudflare/workers-types"],
|
|
3348
|
+
projectDir: packageDir
|
|
3349
|
+
});
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3316
3352
|
|
|
3317
3353
|
//#endregion
|
|
3318
3354
|
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
@@ -3323,7 +3359,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3323
3359
|
dependencies: ["@opennextjs/cloudflare"],
|
|
3324
3360
|
devDependencies: [
|
|
3325
3361
|
"alchemy",
|
|
3326
|
-
"dotenv",
|
|
3327
3362
|
"wrangler",
|
|
3328
3363
|
"@cloudflare/workers-types"
|
|
3329
3364
|
],
|
|
@@ -3359,7 +3394,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3359
3394
|
devDependencies: [
|
|
3360
3395
|
"alchemy",
|
|
3361
3396
|
"nitro-cloudflare-dev",
|
|
3362
|
-
"dotenv",
|
|
3363
3397
|
"wrangler"
|
|
3364
3398
|
],
|
|
3365
3399
|
projectDir: webAppDir
|
|
@@ -3422,7 +3456,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
|
|
|
3422
3456
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3423
3457
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3424
3458
|
await addPackageDependency({
|
|
3425
|
-
devDependencies: ["alchemy"
|
|
3459
|
+
devDependencies: ["alchemy"],
|
|
3426
3460
|
projectDir: webAppDir
|
|
3427
3461
|
});
|
|
3428
3462
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3443,7 +3477,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3443
3477
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3444
3478
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3445
3479
|
await addPackageDependency({
|
|
3446
|
-
devDependencies: ["alchemy"
|
|
3480
|
+
devDependencies: ["alchemy"],
|
|
3447
3481
|
projectDir: webAppDir
|
|
3448
3482
|
});
|
|
3449
3483
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3464,11 +3498,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3464
3498
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3465
3499
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3466
3500
|
await addPackageDependency({
|
|
3467
|
-
devDependencies: [
|
|
3468
|
-
"alchemy",
|
|
3469
|
-
"@sveltejs/adapter-cloudflare",
|
|
3470
|
-
"dotenv"
|
|
3471
|
-
],
|
|
3501
|
+
devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
|
|
3472
3502
|
projectDir: webAppDir
|
|
3473
3503
|
});
|
|
3474
3504
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3533,7 +3563,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
|
|
|
3533
3563
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3534
3564
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3535
3565
|
await addPackageDependency({
|
|
3536
|
-
devDependencies: ["alchemy"
|
|
3566
|
+
devDependencies: ["alchemy"],
|
|
3537
3567
|
projectDir: webAppDir
|
|
3538
3568
|
});
|
|
3539
3569
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3554,11 +3584,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3554
3584
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3555
3585
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3556
3586
|
await addPackageDependency({
|
|
3557
|
-
devDependencies: [
|
|
3558
|
-
"alchemy",
|
|
3559
|
-
"dotenv",
|
|
3560
|
-
"@cloudflare/vite-plugin"
|
|
3561
|
-
],
|
|
3587
|
+
devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
|
|
3562
3588
|
projectDir: webAppDir
|
|
3563
3589
|
});
|
|
3564
3590
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3613,7 +3639,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3613
3639
|
//#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
|
|
3614
3640
|
async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
3615
3641
|
await addPackageDependency({
|
|
3616
|
-
devDependencies: ["alchemy"
|
|
3642
|
+
devDependencies: ["alchemy"],
|
|
3617
3643
|
projectDir
|
|
3618
3644
|
});
|
|
3619
3645
|
const rootPkgPath = path.join(projectDir, "package.json");
|
|
@@ -3628,7 +3654,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3628
3654
|
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3629
3655
|
}
|
|
3630
3656
|
const serverDir = path.join(projectDir, "apps/server");
|
|
3631
|
-
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
|
|
3657
|
+
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
|
|
3632
3658
|
const frontend = config.frontend;
|
|
3633
3659
|
const isNext = frontend.includes("next");
|
|
3634
3660
|
const isNuxt = frontend.includes("nuxt");
|
|
@@ -3861,6 +3887,7 @@ async function setupWebDeploy(config) {
|
|
|
3861
3887
|
if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
|
|
3862
3888
|
if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
|
|
3863
3889
|
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
3890
|
+
await addAlchemyPackagesDependencies(projectDir);
|
|
3864
3891
|
return;
|
|
3865
3892
|
}
|
|
3866
3893
|
const isNext = frontend.includes("next");
|
|
@@ -3884,6 +3911,7 @@ async function setupWebDeploy(config) {
|
|
|
3884
3911
|
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
3885
3912
|
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
3886
3913
|
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
3914
|
+
await addAlchemyPackagesDependencies(projectDir);
|
|
3887
3915
|
}
|
|
3888
3916
|
}
|
|
3889
3917
|
async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
@@ -3901,6 +3929,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
|
3901
3929
|
}
|
|
3902
3930
|
await setupWorkersVitePlugin(projectDir);
|
|
3903
3931
|
}
|
|
3932
|
+
async function addAlchemyPackagesDependencies(projectDir) {
|
|
3933
|
+
for (const packageName of [
|
|
3934
|
+
"packages/api",
|
|
3935
|
+
"packages/auth",
|
|
3936
|
+
"packages/db"
|
|
3937
|
+
]) {
|
|
3938
|
+
const packageDir = path.join(projectDir, packageName);
|
|
3939
|
+
if (await fs.pathExists(packageDir)) await addPackageDependency({
|
|
3940
|
+
devDependencies: ["@cloudflare/workers-types"],
|
|
3941
|
+
projectDir: packageDir
|
|
3942
|
+
});
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3904
3945
|
|
|
3905
3946
|
//#endregion
|
|
3906
3947
|
//#region src/helpers/core/add-deployment.ts
|
|
@@ -4060,15 +4101,30 @@ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
|
|
|
4060
4101
|
//#endregion
|
|
4061
4102
|
//#region src/helpers/addons/examples-setup.ts
|
|
4062
4103
|
async function setupExamples(config) {
|
|
4063
|
-
const { examples, frontend, backend, projectDir } = config;
|
|
4104
|
+
const { examples, frontend, backend, projectDir, orm } = config;
|
|
4064
4105
|
if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
|
|
4106
|
+
const apiDir = path.join(projectDir, "packages/api");
|
|
4107
|
+
if (await fs.pathExists(apiDir) && backend !== "none") {
|
|
4108
|
+
if (orm === "drizzle") await addPackageDependency({
|
|
4109
|
+
dependencies: ["drizzle-orm"],
|
|
4110
|
+
projectDir: apiDir
|
|
4111
|
+
});
|
|
4112
|
+
else if (orm === "prisma") await addPackageDependency({
|
|
4113
|
+
dependencies: ["@prisma/client"],
|
|
4114
|
+
projectDir: apiDir
|
|
4115
|
+
});
|
|
4116
|
+
else if (orm === "mongoose") await addPackageDependency({
|
|
4117
|
+
dependencies: ["mongoose"],
|
|
4118
|
+
projectDir: apiDir
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4065
4121
|
if (examples.includes("ai")) {
|
|
4066
4122
|
const webClientDir = path.join(projectDir, "apps/web");
|
|
4067
4123
|
const nativeClientDir = path.join(projectDir, "apps/native");
|
|
4068
|
-
const apiDir = path.join(projectDir, "packages/api");
|
|
4124
|
+
const apiDir$1 = path.join(projectDir, "packages/api");
|
|
4069
4125
|
const webClientDirExists = await fs.pathExists(webClientDir);
|
|
4070
4126
|
const nativeClientDirExists = await fs.pathExists(nativeClientDir);
|
|
4071
|
-
const apiDirExists = await fs.pathExists(apiDir);
|
|
4127
|
+
const apiDirExists = await fs.pathExists(apiDir$1);
|
|
4072
4128
|
const hasNuxt = frontend.includes("nuxt");
|
|
4073
4129
|
const hasSvelte = frontend.includes("svelte");
|
|
4074
4130
|
const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
|
|
@@ -4091,7 +4147,11 @@ async function setupExamples(config) {
|
|
|
4091
4147
|
});
|
|
4092
4148
|
if (apiDirExists && backend !== "none") await addPackageDependency({
|
|
4093
4149
|
dependencies: ["ai", "@ai-sdk/google"],
|
|
4094
|
-
projectDir: apiDir
|
|
4150
|
+
projectDir: apiDir$1
|
|
4151
|
+
});
|
|
4152
|
+
if (backend === "self" && webClientDirExists) await addPackageDependency({
|
|
4153
|
+
dependencies: ["ai", "@ai-sdk/google"],
|
|
4154
|
+
projectDir: webClientDir
|
|
4095
4155
|
});
|
|
4096
4156
|
}
|
|
4097
4157
|
}
|
|
@@ -4207,7 +4267,7 @@ function getConvexDependencies(frontend) {
|
|
|
4207
4267
|
return deps;
|
|
4208
4268
|
}
|
|
4209
4269
|
async function setupApi(config) {
|
|
4210
|
-
const { api, projectName, frontend, backend, packageManager, projectDir
|
|
4270
|
+
const { api, projectName, frontend, backend, packageManager, projectDir } = config;
|
|
4211
4271
|
const isConvex = backend === "convex";
|
|
4212
4272
|
const webDir = path.join(projectDir, "apps/web");
|
|
4213
4273
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
@@ -4224,40 +4284,18 @@ async function setupApi(config) {
|
|
|
4224
4284
|
dependencies: apiDeps.server.dependencies,
|
|
4225
4285
|
projectDir: apiPackageDir
|
|
4226
4286
|
});
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
else if (backend === "express") frameworkDeps.push("express", "@types/express");
|
|
4231
|
-
else if (backend === "fastify") frameworkDeps.push("fastify");
|
|
4232
|
-
else if (backend === "self") {
|
|
4233
|
-
if (frontend.includes("next")) frameworkDeps.push("next");
|
|
4234
|
-
}
|
|
4235
|
-
if (frameworkDeps.length > 0) await addPackageDependency({
|
|
4236
|
-
dependencies: frameworkDeps,
|
|
4237
|
-
projectDir: apiPackageDir
|
|
4287
|
+
if (backend === "self" && webDirExists) await addPackageDependency({
|
|
4288
|
+
dependencies: apiDeps.server.dependencies,
|
|
4289
|
+
projectDir: webDir
|
|
4238
4290
|
});
|
|
4239
|
-
if (
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
else if (backend === "elysia") await addPackageDependency({
|
|
4245
|
-
dependencies: ["@elysiajs/trpc"],
|
|
4246
|
-
projectDir: apiPackageDir
|
|
4247
|
-
});
|
|
4248
|
-
else if (backend === "express") await addPackageDependency({
|
|
4249
|
-
dependencies: ["@trpc/server"],
|
|
4250
|
-
projectDir: apiPackageDir
|
|
4251
|
-
});
|
|
4252
|
-
else if (backend === "fastify") await addPackageDependency({
|
|
4253
|
-
dependencies: ["@trpc/server"],
|
|
4291
|
+
if (backend === "self") {
|
|
4292
|
+
const frameworkDeps = [];
|
|
4293
|
+
if (frontend.includes("next")) frameworkDeps.push("next");
|
|
4294
|
+
if (frameworkDeps.length > 0) await addPackageDependency({
|
|
4295
|
+
dependencies: frameworkDeps,
|
|
4254
4296
|
projectDir: apiPackageDir
|
|
4255
4297
|
});
|
|
4256
4298
|
}
|
|
4257
|
-
if (auth === "better-auth") await addPackageDependency({
|
|
4258
|
-
dependencies: ["better-auth"],
|
|
4259
|
-
projectDir: apiPackageDir
|
|
4260
|
-
});
|
|
4261
4299
|
}
|
|
4262
4300
|
if (webDirExists && apiDeps.web) await addPackageDependency({
|
|
4263
4301
|
dependencies: apiDeps.web.dependencies,
|
|
@@ -4309,34 +4347,23 @@ async function setupBackendDependencies(config) {
|
|
|
4309
4347
|
const devDependencies = [];
|
|
4310
4348
|
if (framework === "hono") {
|
|
4311
4349
|
dependencies.push("hono");
|
|
4312
|
-
if (
|
|
4313
|
-
if (runtime === "node") {
|
|
4314
|
-
dependencies.push("@hono/node-server");
|
|
4315
|
-
devDependencies.push("tsx", "@types/node");
|
|
4316
|
-
}
|
|
4350
|
+
if (runtime === "node") dependencies.push("@hono/node-server");
|
|
4317
4351
|
} else if (framework === "elysia") {
|
|
4318
4352
|
dependencies.push("elysia", "@elysiajs/cors");
|
|
4319
|
-
if (
|
|
4320
|
-
if (runtime === "node") {
|
|
4321
|
-
dependencies.push("@elysiajs/node");
|
|
4322
|
-
devDependencies.push("tsx", "@types/node");
|
|
4323
|
-
}
|
|
4353
|
+
if (runtime === "node") dependencies.push("@elysiajs/node");
|
|
4324
4354
|
} else if (framework === "express") {
|
|
4325
4355
|
dependencies.push("express", "cors");
|
|
4326
4356
|
devDependencies.push("@types/express", "@types/cors");
|
|
4327
|
-
|
|
4328
|
-
} else if (framework === "fastify") {
|
|
4329
|
-
dependencies.push("fastify", "@fastify/cors");
|
|
4330
|
-
if (runtime === "node") devDependencies.push("tsx", "@types/node");
|
|
4331
|
-
}
|
|
4357
|
+
} else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
|
|
4332
4358
|
if (api === "trpc") {
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
else if (
|
|
4359
|
+
dependencies.push("@trpc/server");
|
|
4360
|
+
if (framework === "hono") dependencies.push("@hono/trpc-server");
|
|
4361
|
+
else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
|
|
4336
4362
|
} else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
|
|
4337
4363
|
if (auth === "better-auth") dependencies.push("better-auth");
|
|
4338
4364
|
if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
|
|
4339
|
-
if (runtime === "
|
|
4365
|
+
if (runtime === "node") devDependencies.push("tsx", "@types/node");
|
|
4366
|
+
else if (runtime === "bun") devDependencies.push("@types/bun");
|
|
4340
4367
|
if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
|
|
4341
4368
|
dependencies,
|
|
4342
4369
|
devDependencies,
|
|
@@ -4457,6 +4484,34 @@ function generateAuthSecret(length = 32) {
|
|
|
4457
4484
|
|
|
4458
4485
|
//#endregion
|
|
4459
4486
|
//#region src/helpers/core/env-setup.ts
|
|
4487
|
+
function getClientServerVar(frontend, backend) {
|
|
4488
|
+
const hasNextJs = frontend.includes("next");
|
|
4489
|
+
const hasNuxt = frontend.includes("nuxt");
|
|
4490
|
+
const hasSvelte = frontend.includes("svelte");
|
|
4491
|
+
if (backend === "self") return {
|
|
4492
|
+
key: "",
|
|
4493
|
+
value: "",
|
|
4494
|
+
write: false
|
|
4495
|
+
};
|
|
4496
|
+
let key = "VITE_SERVER_URL";
|
|
4497
|
+
if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
|
|
4498
|
+
else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
|
|
4499
|
+
else if (hasSvelte) key = "PUBLIC_SERVER_URL";
|
|
4500
|
+
return {
|
|
4501
|
+
key,
|
|
4502
|
+
value: "http://localhost:3000",
|
|
4503
|
+
write: true
|
|
4504
|
+
};
|
|
4505
|
+
}
|
|
4506
|
+
function getConvexVar(frontend) {
|
|
4507
|
+
const hasNextJs = frontend.includes("next");
|
|
4508
|
+
const hasNuxt = frontend.includes("nuxt");
|
|
4509
|
+
const hasSvelte = frontend.includes("svelte");
|
|
4510
|
+
if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
|
|
4511
|
+
if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
|
|
4512
|
+
if (hasSvelte) return "PUBLIC_CONVEX_URL";
|
|
4513
|
+
return "VITE_CONVEX_URL";
|
|
4514
|
+
}
|
|
4460
4515
|
async function addEnvVariablesToFile(filePath, variables) {
|
|
4461
4516
|
await fs.ensureDir(path.dirname(filePath));
|
|
4462
4517
|
let envContent = "";
|
|
@@ -4515,22 +4570,13 @@ async function setupEnvironmentVariables(config) {
|
|
|
4515
4570
|
if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
|
|
4516
4571
|
const clientDir = path.join(projectDir, "apps/web");
|
|
4517
4572
|
if (await fs.pathExists(clientDir)) {
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
else if (hasNuxt) envVarName = "NUXT_PUBLIC_SERVER_URL";
|
|
4522
|
-
else if (hasSvelte) envVarName = "PUBLIC_SERVER_URL";
|
|
4523
|
-
if (backend === "convex") {
|
|
4524
|
-
if (hasNextJs) envVarName = "NEXT_PUBLIC_CONVEX_URL";
|
|
4525
|
-
else if (hasNuxt) envVarName = "NUXT_PUBLIC_CONVEX_URL";
|
|
4526
|
-
else if (hasSvelte) envVarName = "PUBLIC_CONVEX_URL";
|
|
4527
|
-
else envVarName = "VITE_CONVEX_URL";
|
|
4528
|
-
serverUrl = "https://<YOUR_CONVEX_URL>";
|
|
4529
|
-
}
|
|
4573
|
+
const baseVar = getClientServerVar(frontend, backend);
|
|
4574
|
+
const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
|
|
4575
|
+
const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
|
|
4530
4576
|
const clientVars = [{
|
|
4531
4577
|
key: envVarName,
|
|
4532
4578
|
value: serverUrl,
|
|
4533
|
-
condition: true
|
|
4579
|
+
condition: backend === "convex" ? true : baseVar.write
|
|
4534
4580
|
}];
|
|
4535
4581
|
if (backend === "convex" && auth === "clerk") {
|
|
4536
4582
|
if (hasNextJs) clientVars.push({
|
|
@@ -4622,7 +4668,8 @@ async function setupEnvironmentVariables(config) {
|
|
|
4622
4668
|
}
|
|
4623
4669
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4624
4670
|
let corsOrigin = "http://localhost:3001";
|
|
4625
|
-
if (
|
|
4671
|
+
if (backend === "self") corsOrigin = "http://localhost:3001";
|
|
4672
|
+
else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
|
|
4626
4673
|
let databaseUrl = null;
|
|
4627
4674
|
if (database !== "none" && dbSetup === "none") switch (database) {
|
|
4628
4675
|
case "postgres":
|
|
@@ -4636,50 +4683,53 @@ async function setupEnvironmentVariables(config) {
|
|
|
4636
4683
|
break;
|
|
4637
4684
|
case "sqlite":
|
|
4638
4685
|
if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
|
|
4639
|
-
else
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
if (await fs.pathExists(serverDir)) {
|
|
4643
|
-
const serverEnvPath = path.join(serverDir, ".env");
|
|
4644
|
-
const serverVars = [
|
|
4645
|
-
{
|
|
4646
|
-
key: "BETTER_AUTH_SECRET",
|
|
4647
|
-
value: generateAuthSecret(),
|
|
4648
|
-
condition: !!auth
|
|
4649
|
-
},
|
|
4650
|
-
{
|
|
4651
|
-
key: "BETTER_AUTH_URL",
|
|
4652
|
-
value: "http://localhost:3000",
|
|
4653
|
-
condition: !!auth
|
|
4654
|
-
},
|
|
4655
|
-
{
|
|
4656
|
-
key: "POLAR_ACCESS_TOKEN",
|
|
4657
|
-
value: "",
|
|
4658
|
-
condition: config.payments === "polar"
|
|
4659
|
-
},
|
|
4660
|
-
{
|
|
4661
|
-
key: "POLAR_SUCCESS_URL",
|
|
4662
|
-
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
|
|
4663
|
-
condition: config.payments === "polar"
|
|
4664
|
-
},
|
|
4665
|
-
{
|
|
4666
|
-
key: "CORS_ORIGIN",
|
|
4667
|
-
value: corsOrigin,
|
|
4668
|
-
condition: true
|
|
4669
|
-
},
|
|
4670
|
-
{
|
|
4671
|
-
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4672
|
-
value: "",
|
|
4673
|
-
condition: examples?.includes("ai") || false
|
|
4674
|
-
},
|
|
4675
|
-
{
|
|
4676
|
-
key: "DATABASE_URL",
|
|
4677
|
-
value: databaseUrl,
|
|
4678
|
-
condition: database !== "none" && dbSetup === "none"
|
|
4686
|
+
else {
|
|
4687
|
+
const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
|
|
4688
|
+
databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
|
|
4679
4689
|
}
|
|
4680
|
-
|
|
4681
|
-
await addEnvVariablesToFile(serverEnvPath, serverVars);
|
|
4690
|
+
break;
|
|
4682
4691
|
}
|
|
4692
|
+
const serverVars = [
|
|
4693
|
+
{
|
|
4694
|
+
key: "BETTER_AUTH_SECRET",
|
|
4695
|
+
value: generateAuthSecret(),
|
|
4696
|
+
condition: !!auth
|
|
4697
|
+
},
|
|
4698
|
+
{
|
|
4699
|
+
key: "BETTER_AUTH_URL",
|
|
4700
|
+
value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
|
|
4701
|
+
condition: !!auth
|
|
4702
|
+
},
|
|
4703
|
+
{
|
|
4704
|
+
key: "POLAR_ACCESS_TOKEN",
|
|
4705
|
+
value: "",
|
|
4706
|
+
condition: config.payments === "polar"
|
|
4707
|
+
},
|
|
4708
|
+
{
|
|
4709
|
+
key: "POLAR_SUCCESS_URL",
|
|
4710
|
+
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
|
|
4711
|
+
condition: config.payments === "polar"
|
|
4712
|
+
},
|
|
4713
|
+
{
|
|
4714
|
+
key: "CORS_ORIGIN",
|
|
4715
|
+
value: corsOrigin,
|
|
4716
|
+
condition: true
|
|
4717
|
+
},
|
|
4718
|
+
{
|
|
4719
|
+
key: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
4720
|
+
value: "",
|
|
4721
|
+
condition: examples?.includes("ai") || false
|
|
4722
|
+
},
|
|
4723
|
+
{
|
|
4724
|
+
key: "DATABASE_URL",
|
|
4725
|
+
value: databaseUrl,
|
|
4726
|
+
condition: database !== "none" && dbSetup === "none"
|
|
4727
|
+
}
|
|
4728
|
+
];
|
|
4729
|
+
if (backend === "self") {
|
|
4730
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4731
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
|
|
4732
|
+
} else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
|
|
4683
4733
|
const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
|
|
4684
4734
|
const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
|
|
4685
4735
|
if (isUnifiedAlchemy) {
|
|
@@ -4698,20 +4748,27 @@ async function setupEnvironmentVariables(config) {
|
|
|
4698
4748
|
condition: true
|
|
4699
4749
|
}]);
|
|
4700
4750
|
}
|
|
4701
|
-
if (serverDeploy === "alchemy")
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4751
|
+
if (serverDeploy === "alchemy") {
|
|
4752
|
+
const serverAlchemyVars = [{
|
|
4753
|
+
key: "ALCHEMY_PASSWORD",
|
|
4754
|
+
value: "please-change-this",
|
|
4755
|
+
condition: true
|
|
4756
|
+
}];
|
|
4757
|
+
if (backend === "self") {
|
|
4758
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
4759
|
+
if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
|
|
4760
|
+
} else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
|
|
4761
|
+
}
|
|
4706
4762
|
}
|
|
4707
4763
|
}
|
|
4708
4764
|
|
|
4709
4765
|
//#endregion
|
|
4710
4766
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4711
4767
|
async function setupCloudflareD1(config) {
|
|
4712
|
-
const { projectDir, serverDeploy, orm } = config;
|
|
4768
|
+
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4713
4769
|
if (serverDeploy === "wrangler") {
|
|
4714
|
-
const
|
|
4770
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
4771
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4715
4772
|
const variables = [
|
|
4716
4773
|
{
|
|
4717
4774
|
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
@@ -4734,7 +4791,8 @@ async function setupCloudflareD1(config) {
|
|
|
4734
4791
|
} catch (_err) {}
|
|
4735
4792
|
}
|
|
4736
4793
|
if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
|
|
4737
|
-
const
|
|
4794
|
+
const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
|
|
4795
|
+
const envPath = path.join(projectDir, targetApp2, ".env");
|
|
4738
4796
|
const variables = [{
|
|
4739
4797
|
key: "DATABASE_URL",
|
|
4740
4798
|
value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
|
|
@@ -4743,7 +4801,7 @@ async function setupCloudflareD1(config) {
|
|
|
4743
4801
|
try {
|
|
4744
4802
|
await addEnvVariablesToFile(envPath, variables);
|
|
4745
4803
|
} catch (_err) {}
|
|
4746
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4804
|
+
const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
|
|
4747
4805
|
await addPackageDependency({
|
|
4748
4806
|
dependencies: ["@prisma/adapter-d1"],
|
|
4749
4807
|
projectDir: serverDir
|
|
@@ -4794,14 +4852,13 @@ async function commandExists(command) {
|
|
|
4794
4852
|
//#endregion
|
|
4795
4853
|
//#region src/helpers/database-providers/mongodb-atlas-setup.ts
|
|
4796
4854
|
async function checkAtlasCLI() {
|
|
4797
|
-
const s = spinner();
|
|
4798
|
-
s.start("Checking for MongoDB Atlas CLI...");
|
|
4799
4855
|
try {
|
|
4800
4856
|
const exists = await commandExists("atlas");
|
|
4801
|
-
|
|
4857
|
+
if (exists) log.info("MongoDB Atlas CLI found");
|
|
4858
|
+
else log.warn(pc.yellow("MongoDB Atlas CLI not found"));
|
|
4802
4859
|
return exists;
|
|
4803
4860
|
} catch (_error) {
|
|
4804
|
-
|
|
4861
|
+
log.error(pc.red("Error checking MongoDB Atlas CLI"));
|
|
4805
4862
|
return false;
|
|
4806
4863
|
}
|
|
4807
4864
|
}
|
|
@@ -4812,12 +4869,13 @@ async function initMongoDBAtlas(serverDir) {
|
|
|
4812
4869
|
log.info(pc.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/"));
|
|
4813
4870
|
return null;
|
|
4814
4871
|
}
|
|
4815
|
-
log.info(
|
|
4872
|
+
log.info("Running MongoDB Atlas setup...");
|
|
4816
4873
|
await execa("atlas", ["deployments", "setup"], {
|
|
4817
4874
|
cwd: serverDir,
|
|
4875
|
+
shell: true,
|
|
4818
4876
|
stdio: "inherit"
|
|
4819
4877
|
});
|
|
4820
|
-
log.
|
|
4878
|
+
log.success("MongoDB Atlas deployment ready");
|
|
4821
4879
|
const connectionString = await text({
|
|
4822
4880
|
message: "Enter your MongoDB connection string:",
|
|
4823
4881
|
placeholder: "mongodb+srv://username:password@cluster.mongodb.net/database",
|
|
@@ -4836,9 +4894,10 @@ async function initMongoDBAtlas(serverDir) {
|
|
|
4836
4894
|
return null;
|
|
4837
4895
|
}
|
|
4838
4896
|
}
|
|
4839
|
-
async function writeEnvFile$3(projectDir, config) {
|
|
4897
|
+
async function writeEnvFile$3(projectDir, backend, config) {
|
|
4840
4898
|
try {
|
|
4841
|
-
const
|
|
4899
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
4900
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4842
4901
|
const variables = [{
|
|
4843
4902
|
key: "DATABASE_URL",
|
|
4844
4903
|
value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
|
|
@@ -4867,16 +4926,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
|
|
|
4867
4926
|
`);
|
|
4868
4927
|
}
|
|
4869
4928
|
async function setupMongoDBAtlas(config, cliInput) {
|
|
4870
|
-
const { projectDir } = config;
|
|
4929
|
+
const { projectDir, backend } = config;
|
|
4871
4930
|
const manualDb = cliInput?.manualDb ?? false;
|
|
4872
|
-
const
|
|
4873
|
-
mainSpinner.start("Setting up MongoDB Atlas...");
|
|
4874
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4931
|
+
const serverDir = path.join(projectDir, "packages/db");
|
|
4875
4932
|
try {
|
|
4876
4933
|
await fs.ensureDir(serverDir);
|
|
4877
4934
|
if (manualDb) {
|
|
4878
|
-
|
|
4879
|
-
await writeEnvFile$3(projectDir);
|
|
4935
|
+
log.info("MongoDB Atlas manual setup selected");
|
|
4936
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4880
4937
|
displayManualSetupInstructions$3();
|
|
4881
4938
|
return;
|
|
4882
4939
|
}
|
|
@@ -4895,26 +4952,24 @@ async function setupMongoDBAtlas(config, cliInput) {
|
|
|
4895
4952
|
});
|
|
4896
4953
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
4897
4954
|
if (mode === "manual") {
|
|
4898
|
-
|
|
4899
|
-
await writeEnvFile$3(projectDir);
|
|
4955
|
+
log.info("MongoDB Atlas manual setup selected");
|
|
4956
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4900
4957
|
displayManualSetupInstructions$3();
|
|
4901
4958
|
return;
|
|
4902
4959
|
}
|
|
4903
|
-
mainSpinner.stop("MongoDB Atlas setup ready");
|
|
4904
4960
|
const config$1 = await initMongoDBAtlas(serverDir);
|
|
4905
4961
|
if (config$1) {
|
|
4906
|
-
await writeEnvFile$3(projectDir, config$1);
|
|
4962
|
+
await writeEnvFile$3(projectDir, backend, config$1);
|
|
4907
4963
|
log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
|
|
4908
4964
|
} else {
|
|
4909
4965
|
log.warn(pc.yellow("Falling back to local MongoDB configuration"));
|
|
4910
|
-
await writeEnvFile$3(projectDir);
|
|
4966
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4911
4967
|
displayManualSetupInstructions$3();
|
|
4912
4968
|
}
|
|
4913
4969
|
} catch (error) {
|
|
4914
|
-
mainSpinner.stop(pc.red("MongoDB Atlas setup failed"));
|
|
4915
4970
|
consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
4916
4971
|
try {
|
|
4917
|
-
await writeEnvFile$3(projectDir);
|
|
4972
|
+
await writeEnvFile$3(projectDir, backend);
|
|
4918
4973
|
displayManualSetupInstructions$3();
|
|
4919
4974
|
} catch {}
|
|
4920
4975
|
}
|
|
@@ -4971,7 +5026,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
|
|
|
4971
5026
|
}
|
|
4972
5027
|
async function createNeonProject(projectName, regionId, packageManager) {
|
|
4973
5028
|
try {
|
|
4974
|
-
const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
|
|
5029
|
+
const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
|
|
4975
5030
|
const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
|
|
4976
5031
|
const response = JSON.parse(stdout);
|
|
4977
5032
|
if (response.project && response.connection_uris && response.connection_uris.length > 0) {
|
|
@@ -4991,8 +5046,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
|
|
|
4991
5046
|
consola$1.error(pc.red("Failed to create Neon project"));
|
|
4992
5047
|
}
|
|
4993
5048
|
}
|
|
4994
|
-
async function writeEnvFile$2(projectDir, config) {
|
|
4995
|
-
const
|
|
5049
|
+
async function writeEnvFile$2(projectDir, backend, config) {
|
|
5050
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5051
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4996
5052
|
const variables = [{
|
|
4997
5053
|
key: "DATABASE_URL",
|
|
4998
5054
|
value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
|
|
@@ -5001,16 +5057,17 @@ async function writeEnvFile$2(projectDir, config) {
|
|
|
5001
5057
|
await addEnvVariablesToFile(envPath, variables);
|
|
5002
5058
|
return true;
|
|
5003
5059
|
}
|
|
5004
|
-
async function setupWithNeonDb(projectDir, packageManager) {
|
|
5060
|
+
async function setupWithNeonDb(projectDir, packageManager, backend) {
|
|
5005
5061
|
try {
|
|
5006
5062
|
const s = spinner();
|
|
5007
5063
|
s.start("Creating Neon database using neondb...");
|
|
5008
|
-
const
|
|
5009
|
-
|
|
5010
|
-
|
|
5064
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5065
|
+
const targetDir = path.join(projectDir, targetApp);
|
|
5066
|
+
await fs.ensureDir(targetDir);
|
|
5067
|
+
const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
|
|
5011
5068
|
await execa(packageCmd, {
|
|
5012
5069
|
shell: true,
|
|
5013
|
-
cwd:
|
|
5070
|
+
cwd: targetDir
|
|
5014
5071
|
});
|
|
5015
5072
|
s.stop(pc.green("Neon database created successfully!"));
|
|
5016
5073
|
return true;
|
|
@@ -5019,23 +5076,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
|
|
|
5019
5076
|
throw error;
|
|
5020
5077
|
}
|
|
5021
5078
|
}
|
|
5022
|
-
function displayManualSetupInstructions$2() {
|
|
5079
|
+
function displayManualSetupInstructions$2(target) {
|
|
5023
5080
|
log.info(`Manual Neon PostgreSQL Setup Instructions:
|
|
5024
5081
|
|
|
5025
5082
|
1. Visit https://neon.tech and create an account
|
|
5026
5083
|
2. Create a new project from the dashboard
|
|
5027
5084
|
3. Get your connection string
|
|
5028
|
-
4. Add the database URL to the .env file in
|
|
5085
|
+
4. Add the database URL to the .env file in ${target}/.env
|
|
5029
5086
|
|
|
5030
5087
|
DATABASE_URL="your_connection_string"`);
|
|
5031
5088
|
}
|
|
5032
5089
|
async function setupNeonPostgres(config, cliInput) {
|
|
5033
|
-
const { packageManager, projectDir } = config;
|
|
5090
|
+
const { packageManager, projectDir, backend } = config;
|
|
5034
5091
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5035
5092
|
try {
|
|
5036
5093
|
if (manualDb) {
|
|
5037
|
-
await writeEnvFile$2(projectDir);
|
|
5038
|
-
displayManualSetupInstructions$2();
|
|
5094
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5095
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5039
5096
|
return;
|
|
5040
5097
|
}
|
|
5041
5098
|
const mode = await select({
|
|
@@ -5053,8 +5110,8 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5053
5110
|
});
|
|
5054
5111
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5055
5112
|
if (mode === "manual") {
|
|
5056
|
-
await writeEnvFile$2(projectDir);
|
|
5057
|
-
displayManualSetupInstructions$2();
|
|
5113
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5114
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5058
5115
|
return;
|
|
5059
5116
|
}
|
|
5060
5117
|
const setupMethod = await select({
|
|
@@ -5071,7 +5128,7 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5071
5128
|
initialValue: "neondb"
|
|
5072
5129
|
});
|
|
5073
5130
|
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
5074
|
-
if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
|
|
5131
|
+
if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
|
|
5075
5132
|
else {
|
|
5076
5133
|
const suggestedProjectName = path.basename(projectDir);
|
|
5077
5134
|
const projectName = await text({
|
|
@@ -5089,22 +5146,22 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5089
5146
|
if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
|
|
5090
5147
|
const finalSpinner = spinner();
|
|
5091
5148
|
finalSpinner.start("Configuring database connection");
|
|
5092
|
-
await
|
|
5093
|
-
await writeEnvFile$2(projectDir, neonConfig);
|
|
5149
|
+
await writeEnvFile$2(projectDir, backend, neonConfig);
|
|
5094
5150
|
finalSpinner.stop("Neon database configured!");
|
|
5095
5151
|
}
|
|
5096
5152
|
} catch (error) {
|
|
5097
5153
|
if (error instanceof Error) consola$1.error(pc.red(error.message));
|
|
5098
|
-
await writeEnvFile$2(projectDir);
|
|
5099
|
-
displayManualSetupInstructions$2();
|
|
5154
|
+
await writeEnvFile$2(projectDir, backend);
|
|
5155
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5100
5156
|
}
|
|
5101
5157
|
}
|
|
5102
5158
|
|
|
5103
5159
|
//#endregion
|
|
5104
5160
|
//#region src/helpers/database-providers/planetscale-setup.ts
|
|
5105
5161
|
async function setupPlanetScale(config) {
|
|
5106
|
-
const { projectDir, database, orm } = config;
|
|
5107
|
-
const
|
|
5162
|
+
const { projectDir, database, orm, backend } = config;
|
|
5163
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5164
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5108
5165
|
if (database === "mysql" && orm === "drizzle") {
|
|
5109
5166
|
const variables = [
|
|
5110
5167
|
{
|
|
@@ -5128,7 +5185,7 @@ async function setupPlanetScale(config) {
|
|
|
5128
5185
|
condition: true
|
|
5129
5186
|
}
|
|
5130
5187
|
];
|
|
5131
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5188
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
5132
5189
|
await addEnvVariablesToFile(envPath, variables);
|
|
5133
5190
|
}
|
|
5134
5191
|
if (database === "postgres" && orm === "prisma") {
|
|
@@ -5137,7 +5194,7 @@ async function setupPlanetScale(config) {
|
|
|
5137
5194
|
value: "postgresql://username:password@host/database?sslaccept=strict",
|
|
5138
5195
|
condition: true
|
|
5139
5196
|
}];
|
|
5140
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5197
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
5141
5198
|
await addEnvVariablesToFile(envPath, variables);
|
|
5142
5199
|
}
|
|
5143
5200
|
if (database === "postgres" && orm === "drizzle") {
|
|
@@ -5146,7 +5203,7 @@ async function setupPlanetScale(config) {
|
|
|
5146
5203
|
value: "postgresql://username:password@host/database?sslmode=verify-full",
|
|
5147
5204
|
condition: true
|
|
5148
5205
|
}];
|
|
5149
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5206
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
5150
5207
|
await addEnvVariablesToFile(envPath, variables);
|
|
5151
5208
|
}
|
|
5152
5209
|
if (database === "mysql" && orm === "prisma") {
|
|
@@ -5155,7 +5212,7 @@ async function setupPlanetScale(config) {
|
|
|
5155
5212
|
value: "mysql://username:password@host/database?sslaccept=strict",
|
|
5156
5213
|
condition: true
|
|
5157
5214
|
}];
|
|
5158
|
-
await fs.ensureDir(path.join(projectDir,
|
|
5215
|
+
await fs.ensureDir(path.join(projectDir, targetApp));
|
|
5159
5216
|
await addEnvVariablesToFile(envPath, variables);
|
|
5160
5217
|
}
|
|
5161
5218
|
}
|
|
@@ -5225,7 +5282,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
|
|
|
5225
5282
|
try {
|
|
5226
5283
|
const prismaDir = path.join(serverDir, "prisma");
|
|
5227
5284
|
await fs.ensureDir(prismaDir);
|
|
5228
|
-
log.info("Starting Prisma PostgreSQL setup.
|
|
5285
|
+
log.info("Starting Prisma PostgreSQL setup.");
|
|
5229
5286
|
const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
|
|
5230
5287
|
await execa(prismaInitCommand, {
|
|
5231
5288
|
cwd: serverDir,
|
|
@@ -5247,9 +5304,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
|
|
|
5247
5304
|
return null;
|
|
5248
5305
|
}
|
|
5249
5306
|
}
|
|
5250
|
-
async function writeEnvFile$1(projectDir, config) {
|
|
5307
|
+
async function writeEnvFile$1(projectDir, backend, config) {
|
|
5251
5308
|
try {
|
|
5252
|
-
const
|
|
5309
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5310
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5253
5311
|
const variables = [{
|
|
5254
5312
|
key: "DATABASE_URL",
|
|
5255
5313
|
value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
|
|
@@ -5265,23 +5323,23 @@ async function writeEnvFile$1(projectDir, config) {
|
|
|
5265
5323
|
consola$1.error("Failed to update environment configuration");
|
|
5266
5324
|
}
|
|
5267
5325
|
}
|
|
5268
|
-
async function addDotenvImportToPrismaConfig(projectDir) {
|
|
5326
|
+
async function addDotenvImportToPrismaConfig(projectDir, backend) {
|
|
5269
5327
|
try {
|
|
5270
5328
|
const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
|
|
5271
5329
|
let content = await fs.readFile(prismaConfigPath, "utf8");
|
|
5272
|
-
content = `import dotenv from "dotenv";\ndotenv.config({ path: "../../apps/server/.env" });\n${content}`;
|
|
5330
|
+
content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
|
|
5273
5331
|
await fs.writeFile(prismaConfigPath, content);
|
|
5274
5332
|
} catch (_error) {
|
|
5275
5333
|
consola$1.error("Failed to update prisma.config.ts");
|
|
5276
5334
|
}
|
|
5277
5335
|
}
|
|
5278
|
-
function displayManualSetupInstructions$1() {
|
|
5336
|
+
function displayManualSetupInstructions$1(target) {
|
|
5279
5337
|
log.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
5280
5338
|
|
|
5281
5339
|
1. Visit https://console.prisma.io and create an account
|
|
5282
5340
|
2. Create a new PostgreSQL database from the dashboard
|
|
5283
5341
|
3. Get your database URL
|
|
5284
|
-
4. Add the database URL to the .env file in
|
|
5342
|
+
4. Add the database URL to the .env file in ${target}/.env
|
|
5285
5343
|
|
|
5286
5344
|
DATABASE_URL="your_database_url"`);
|
|
5287
5345
|
}
|
|
@@ -5299,14 +5357,14 @@ async function addPrismaAccelerateExtension(projectDir) {
|
|
|
5299
5357
|
}
|
|
5300
5358
|
}
|
|
5301
5359
|
async function setupPrismaPostgres(config, cliInput) {
|
|
5302
|
-
const { packageManager, projectDir, orm } = config;
|
|
5360
|
+
const { packageManager, projectDir, orm, backend } = config;
|
|
5303
5361
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5304
|
-
const
|
|
5362
|
+
const dbDir = path.join(projectDir, "packages/db");
|
|
5305
5363
|
try {
|
|
5306
|
-
await fs.ensureDir(
|
|
5364
|
+
await fs.ensureDir(dbDir);
|
|
5307
5365
|
if (manualDb) {
|
|
5308
|
-
await writeEnvFile$1(projectDir);
|
|
5309
|
-
displayManualSetupInstructions$1();
|
|
5366
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5367
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5310
5368
|
return;
|
|
5311
5369
|
}
|
|
5312
5370
|
const mode = await select({
|
|
@@ -5324,8 +5382,8 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5324
5382
|
});
|
|
5325
5383
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5326
5384
|
if (mode === "manual") {
|
|
5327
|
-
await writeEnvFile$1(projectDir);
|
|
5328
|
-
displayManualSetupInstructions$1();
|
|
5385
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5386
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5329
5387
|
return;
|
|
5330
5388
|
}
|
|
5331
5389
|
const setupOptions = [{
|
|
@@ -5345,26 +5403,26 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5345
5403
|
});
|
|
5346
5404
|
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
5347
5405
|
let prismaConfig = null;
|
|
5348
|
-
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(
|
|
5349
|
-
else prismaConfig = await initPrismaDatabase(
|
|
5406
|
+
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
|
|
5407
|
+
else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
|
|
5350
5408
|
if (prismaConfig) {
|
|
5351
|
-
await writeEnvFile$1(projectDir, prismaConfig);
|
|
5409
|
+
await writeEnvFile$1(projectDir, backend, prismaConfig);
|
|
5352
5410
|
if (orm === "prisma") {
|
|
5353
|
-
await addDotenvImportToPrismaConfig(projectDir);
|
|
5411
|
+
await addDotenvImportToPrismaConfig(projectDir, backend);
|
|
5354
5412
|
await addPrismaAccelerateExtension(projectDir);
|
|
5355
5413
|
}
|
|
5356
5414
|
const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
|
|
5357
5415
|
log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
|
|
5358
5416
|
if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
|
|
5359
5417
|
} else {
|
|
5360
|
-
await writeEnvFile$1(projectDir);
|
|
5361
|
-
displayManualSetupInstructions$1();
|
|
5418
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5419
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5362
5420
|
}
|
|
5363
5421
|
} catch (error) {
|
|
5364
5422
|
consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5365
5423
|
try {
|
|
5366
|
-
await writeEnvFile$1(projectDir);
|
|
5367
|
-
displayManualSetupInstructions$1();
|
|
5424
|
+
await writeEnvFile$1(projectDir, backend);
|
|
5425
|
+
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5368
5426
|
} catch {}
|
|
5369
5427
|
log.info("Setup completed with manual configuration required.");
|
|
5370
5428
|
}
|
|
@@ -5372,9 +5430,10 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5372
5430
|
|
|
5373
5431
|
//#endregion
|
|
5374
5432
|
//#region src/helpers/database-providers/supabase-setup.ts
|
|
5375
|
-
async function writeSupabaseEnvFile(projectDir, databaseUrl) {
|
|
5433
|
+
async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
|
|
5376
5434
|
try {
|
|
5377
|
-
const
|
|
5435
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5436
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5378
5437
|
const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
|
|
5379
5438
|
await addEnvVariablesToFile(envPath, [{
|
|
5380
5439
|
key: "DATABASE_URL",
|
|
@@ -5460,14 +5519,14 @@ ${pc.dim(output)}` : ""}
|
|
|
5460
5519
|
${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
|
|
5461
5520
|
}
|
|
5462
5521
|
async function setupSupabase(config, cliInput) {
|
|
5463
|
-
const { projectDir, packageManager } = config;
|
|
5522
|
+
const { projectDir, packageManager, backend } = config;
|
|
5464
5523
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5465
5524
|
const serverDir = path.join(projectDir, "packages", "db");
|
|
5466
5525
|
try {
|
|
5467
5526
|
await fs.ensureDir(serverDir);
|
|
5468
5527
|
if (manualDb) {
|
|
5469
5528
|
displayManualSupabaseInstructions();
|
|
5470
|
-
await writeSupabaseEnvFile(projectDir, "");
|
|
5529
|
+
await writeSupabaseEnvFile(projectDir, backend, "");
|
|
5471
5530
|
return;
|
|
5472
5531
|
}
|
|
5473
5532
|
const mode = await select({
|
|
@@ -5486,7 +5545,7 @@ async function setupSupabase(config, cliInput) {
|
|
|
5486
5545
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5487
5546
|
if (mode === "manual") {
|
|
5488
5547
|
displayManualSupabaseInstructions();
|
|
5489
|
-
await writeSupabaseEnvFile(projectDir, "");
|
|
5548
|
+
await writeSupabaseEnvFile(projectDir, backend, "");
|
|
5490
5549
|
return;
|
|
5491
5550
|
}
|
|
5492
5551
|
if (!await initializeSupabase(serverDir, packageManager)) {
|
|
@@ -5499,7 +5558,7 @@ async function setupSupabase(config, cliInput) {
|
|
|
5499
5558
|
return;
|
|
5500
5559
|
}
|
|
5501
5560
|
const dbUrl = extractDbUrl(supabaseOutput);
|
|
5502
|
-
if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
|
|
5561
|
+
if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
|
|
5503
5562
|
else {
|
|
5504
5563
|
log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
|
|
5505
5564
|
displayManualSupabaseInstructions(supabaseOutput);
|
|
@@ -5627,8 +5686,9 @@ async function createTursoDatabase(dbName, groupName) {
|
|
|
5627
5686
|
s.stop(pc.red("Failed to retrieve database connection details"));
|
|
5628
5687
|
}
|
|
5629
5688
|
}
|
|
5630
|
-
async function writeEnvFile(projectDir, config) {
|
|
5631
|
-
const
|
|
5689
|
+
async function writeEnvFile(projectDir, backend, config) {
|
|
5690
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5691
|
+
const envPath = path.join(projectDir, targetApp, ".env");
|
|
5632
5692
|
const variables = [{
|
|
5633
5693
|
key: "DATABASE_URL",
|
|
5634
5694
|
value: config?.dbUrl ?? "",
|
|
@@ -5652,12 +5712,12 @@ DATABASE_URL=your_database_url
|
|
|
5652
5712
|
DATABASE_AUTH_TOKEN=your_auth_token`);
|
|
5653
5713
|
}
|
|
5654
5714
|
async function setupTurso(config, cliInput) {
|
|
5655
|
-
const { orm, projectDir } = config;
|
|
5715
|
+
const { orm, projectDir, backend } = config;
|
|
5656
5716
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5657
5717
|
const setupSpinner = spinner();
|
|
5658
5718
|
try {
|
|
5659
5719
|
if (manualDb) {
|
|
5660
|
-
await writeEnvFile(projectDir);
|
|
5720
|
+
await writeEnvFile(projectDir, backend);
|
|
5661
5721
|
displayManualSetupInstructions();
|
|
5662
5722
|
return;
|
|
5663
5723
|
}
|
|
@@ -5676,7 +5736,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5676
5736
|
});
|
|
5677
5737
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5678
5738
|
if (mode === "manual") {
|
|
5679
|
-
await writeEnvFile(projectDir);
|
|
5739
|
+
await writeEnvFile(projectDir, backend);
|
|
5680
5740
|
displayManualSetupInstructions();
|
|
5681
5741
|
return;
|
|
5682
5742
|
}
|
|
@@ -5686,7 +5746,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5686
5746
|
if (platform === "win32") {
|
|
5687
5747
|
if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
5688
5748
|
log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
|
|
5689
|
-
await writeEnvFile(projectDir);
|
|
5749
|
+
await writeEnvFile(projectDir, backend);
|
|
5690
5750
|
displayManualSetupInstructions();
|
|
5691
5751
|
return;
|
|
5692
5752
|
}
|
|
@@ -5698,7 +5758,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5698
5758
|
});
|
|
5699
5759
|
if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
|
|
5700
5760
|
if (!shouldInstall) {
|
|
5701
|
-
await writeEnvFile(projectDir);
|
|
5761
|
+
await writeEnvFile(projectDir, backend);
|
|
5702
5762
|
displayManualSetupInstructions();
|
|
5703
5763
|
return;
|
|
5704
5764
|
}
|
|
@@ -5720,7 +5780,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5720
5780
|
dbName = dbNameResponse;
|
|
5721
5781
|
try {
|
|
5722
5782
|
const config$1 = await createTursoDatabase(dbName, selectedGroup);
|
|
5723
|
-
await writeEnvFile(projectDir, config$1);
|
|
5783
|
+
await writeEnvFile(projectDir, backend, config$1);
|
|
5724
5784
|
success = true;
|
|
5725
5785
|
} catch (error) {
|
|
5726
5786
|
if (error instanceof Error && error.message === "DATABASE_EXISTS") {
|
|
@@ -5733,7 +5793,7 @@ async function setupTurso(config, cliInput) {
|
|
|
5733
5793
|
} catch (error) {
|
|
5734
5794
|
if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
|
|
5735
5795
|
consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5736
|
-
await writeEnvFile(projectDir);
|
|
5796
|
+
await writeEnvFile(projectDir, backend);
|
|
5737
5797
|
displayManualSetupInstructions();
|
|
5738
5798
|
log.success("Setup completed with manual configuration required.");
|
|
5739
5799
|
}
|
|
@@ -5755,32 +5815,36 @@ async function setupDatabase(config, cliInput) {
|
|
|
5755
5815
|
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5756
5816
|
if (!await fs.pathExists(dbPackageDir)) return;
|
|
5757
5817
|
try {
|
|
5758
|
-
if (orm === "prisma")
|
|
5759
|
-
|
|
5760
|
-
"@prisma/client",
|
|
5761
|
-
"@prisma/adapter-planetscale",
|
|
5762
|
-
"@planetscale/database"
|
|
5763
|
-
],
|
|
5764
|
-
devDependencies: ["prisma"],
|
|
5765
|
-
projectDir: dbPackageDir
|
|
5766
|
-
});
|
|
5767
|
-
else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
|
|
5768
|
-
dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
|
|
5769
|
-
devDependencies: ["prisma"],
|
|
5770
|
-
projectDir: dbPackageDir
|
|
5771
|
-
});
|
|
5772
|
-
else await addPackageDependency({
|
|
5773
|
-
dependencies: ["@prisma/client"],
|
|
5774
|
-
devDependencies: ["prisma"],
|
|
5775
|
-
projectDir: dbPackageDir
|
|
5776
|
-
});
|
|
5777
|
-
else if (orm === "drizzle") {
|
|
5778
|
-
if (database === "sqlite") await addPackageDependency({
|
|
5818
|
+
if (orm === "prisma") {
|
|
5819
|
+
if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
|
|
5779
5820
|
dependencies: [
|
|
5780
|
-
"
|
|
5781
|
-
"@
|
|
5782
|
-
"
|
|
5821
|
+
"@prisma/client",
|
|
5822
|
+
"@prisma/adapter-planetscale",
|
|
5823
|
+
"@planetscale/database"
|
|
5783
5824
|
],
|
|
5825
|
+
devDependencies: ["prisma"],
|
|
5826
|
+
projectDir: dbPackageDir
|
|
5827
|
+
});
|
|
5828
|
+
else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
|
|
5829
|
+
dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
|
|
5830
|
+
devDependencies: ["prisma"],
|
|
5831
|
+
projectDir: dbPackageDir
|
|
5832
|
+
});
|
|
5833
|
+
else await addPackageDependency({
|
|
5834
|
+
dependencies: ["@prisma/client"],
|
|
5835
|
+
devDependencies: ["prisma"],
|
|
5836
|
+
projectDir: dbPackageDir
|
|
5837
|
+
});
|
|
5838
|
+
if (backend === "self") {
|
|
5839
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
5840
|
+
if (await fs.pathExists(webDir)) await addPackageDependency({
|
|
5841
|
+
dependencies: ["@prisma/client"],
|
|
5842
|
+
projectDir: webDir
|
|
5843
|
+
});
|
|
5844
|
+
}
|
|
5845
|
+
} else if (orm === "drizzle") {
|
|
5846
|
+
if (database === "sqlite") await addPackageDependency({
|
|
5847
|
+
dependencies: ["drizzle-orm", "@libsql/client"],
|
|
5784
5848
|
devDependencies: ["drizzle-kit"],
|
|
5785
5849
|
projectDir: dbPackageDir
|
|
5786
5850
|
});
|
|
@@ -5929,7 +5993,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
|
|
|
5929
5993
|
${packageManagerRunCmd} dev:setup
|
|
5930
5994
|
\`\`\`
|
|
5931
5995
|
|
|
5932
|
-
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)}
|
|
5996
|
+
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)}
|
|
5933
5997
|
|
|
5934
5998
|
Then, run the development server:
|
|
5935
5999
|
|
|
@@ -5982,6 +6046,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
|
|
|
5982
6046
|
const instructions = [];
|
|
5983
6047
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
5984
6048
|
const isBackendNone = backend === "none";
|
|
6049
|
+
const isBackendSelf = backend === "self";
|
|
5985
6050
|
if (!hasFrontendNone) {
|
|
5986
6051
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
5987
6052
|
const hasReactRouter = frontend.includes("react-router");
|
|
@@ -5990,17 +6055,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
|
|
|
5990
6055
|
const hasSvelte = frontend.includes("svelte");
|
|
5991
6056
|
const hasNuxt = frontend.includes("nuxt");
|
|
5992
6057
|
const hasSolid = frontend.includes("solid");
|
|
5993
|
-
if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see
|
|
6058
|
+
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.`);
|
|
6059
|
+
else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
|
|
5994
6060
|
}
|
|
5995
6061
|
if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
|
|
5996
6062
|
if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
|
|
5997
|
-
else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
6063
|
+
else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
5998
6064
|
return instructions.join("\n");
|
|
5999
6065
|
}
|
|
6000
6066
|
function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
|
|
6001
6067
|
const structure = [`${projectName}/`, "├── apps/"];
|
|
6002
6068
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
6003
6069
|
const isBackendNone = backend === "none";
|
|
6070
|
+
const isBackendSelf = backend === "self";
|
|
6004
6071
|
if (!hasFrontendNone) {
|
|
6005
6072
|
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
6006
6073
|
const hasReactRouter = frontend.includes("react-router");
|
|
@@ -6018,21 +6085,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
|
|
|
6018
6085
|
else if (hasSvelte) frontendType = "SvelteKit";
|
|
6019
6086
|
else if (hasNuxt) frontendType = "Nuxt";
|
|
6020
6087
|
else if (hasSolid) frontendType = "SolidJS";
|
|
6021
|
-
structure.push(`│
|
|
6088
|
+
if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
|
|
6089
|
+
else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
|
|
6022
6090
|
}
|
|
6023
6091
|
}
|
|
6024
|
-
if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
6025
|
-
|
|
6026
|
-
if (
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
|
|
6030
|
-
} else if (!isBackendNone) {
|
|
6092
|
+
if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
6093
|
+
else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
|
|
6094
|
+
if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
|
|
6095
|
+
else structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
|
|
6096
|
+
if (!isBackendSelf && !isBackendNone && !isConvex) {
|
|
6031
6097
|
const backendName = backend[0].toUpperCase() + backend.slice(1);
|
|
6032
6098
|
const apiName = api !== "none" ? api.toUpperCase() : "";
|
|
6033
6099
|
const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
|
|
6034
6100
|
structure.push(`│ └── server/ # Backend API (${backendDesc})`);
|
|
6035
6101
|
}
|
|
6102
|
+
if (isConvex || !isBackendNone) {
|
|
6103
|
+
structure.push("├── packages/");
|
|
6104
|
+
if (isConvex) {
|
|
6105
|
+
structure.push("│ ├── backend/ # Convex backend functions and schema");
|
|
6106
|
+
if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
|
|
6107
|
+
}
|
|
6108
|
+
if (!isConvex) {
|
|
6109
|
+
structure.push("│ ├── api/ # API layer / business logic");
|
|
6110
|
+
if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
|
|
6111
|
+
if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6036
6114
|
return structure.join("\n");
|
|
6037
6115
|
}
|
|
6038
6116
|
function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
|
|
@@ -6090,33 +6168,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
6090
6168
|
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
6091
6169
|
return addonsList.join("\n");
|
|
6092
6170
|
}
|
|
6093
|
-
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
|
|
6171
|
+
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
|
|
6094
6172
|
if (database === "none") return "";
|
|
6173
|
+
const isBackendSelf = backend === "self";
|
|
6174
|
+
const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
|
|
6175
|
+
const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
|
|
6095
6176
|
let setup = "## Database Setup\n\n";
|
|
6096
6177
|
if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6097
6178
|
|
|
6098
6179
|
1. Start the local SQLite database:
|
|
6099
6180
|
${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
|
|
6100
|
-
cd
|
|
6181
|
+
cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
|
|
6101
6182
|
\`\`\`
|
|
6102
6183
|
`}
|
|
6103
6184
|
|
|
6104
|
-
2. Update your \`.env\` file in the
|
|
6185
|
+
2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
|
|
6105
6186
|
`;
|
|
6106
6187
|
else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6107
6188
|
|
|
6108
6189
|
1. Make sure you have a PostgreSQL database set up.
|
|
6109
|
-
2. Update your \`
|
|
6190
|
+
2. Update your \`${envPath}\` file with your PostgreSQL connection details.
|
|
6110
6191
|
`;
|
|
6111
6192
|
else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6112
6193
|
|
|
6113
6194
|
1. Make sure you have a MySQL database set up.
|
|
6114
|
-
2. Update your \`
|
|
6195
|
+
2. Update your \`${envPath}\` file with your MySQL connection details.
|
|
6115
6196
|
`;
|
|
6116
6197
|
else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
|
|
6117
6198
|
|
|
6118
6199
|
1. Make sure you have MongoDB set up.
|
|
6119
|
-
2. Update your \`
|
|
6200
|
+
2. Update your \`${envPath}\` file with your MongoDB connection URI.
|
|
6120
6201
|
`;
|
|
6121
6202
|
setup += `
|
|
6122
6203
|
3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
|
|
@@ -6135,13 +6216,14 @@ ${packageManagerRunCmd} db:push
|
|
|
6135
6216
|
function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
|
|
6136
6217
|
const isConvex = backend === "convex";
|
|
6137
6218
|
const isBackendNone = backend === "none";
|
|
6219
|
+
const isBackendSelf = backend === "self";
|
|
6138
6220
|
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
|
|
6139
6221
|
- \`${packageManagerRunCmd} build\`: Build all applications`;
|
|
6140
|
-
scripts += `
|
|
6222
|
+
if (!isBackendSelf) scripts += `
|
|
6141
6223
|
- \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
|
|
6142
6224
|
if (isConvex) scripts += `
|
|
6143
6225
|
- \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
|
|
6144
|
-
else if (!isBackendNone) scripts += `
|
|
6226
|
+
else if (!isBackendNone && !isBackendSelf) scripts += `
|
|
6145
6227
|
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
|
|
6146
6228
|
scripts += `
|
|
6147
6229
|
- \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
|
|
@@ -6152,7 +6234,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
|
|
|
6152
6234
|
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
|
|
6153
6235
|
- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
|
|
6154
6236
|
if (database === "sqlite" && orm === "drizzle") scripts += `
|
|
6155
|
-
- \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
|
|
6237
|
+
- \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
|
|
6156
6238
|
}
|
|
6157
6239
|
if (addons.includes("biome")) scripts += `
|
|
6158
6240
|
- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
|
|
@@ -6228,15 +6310,14 @@ async function initializeGit(projectDir, useGit) {
|
|
|
6228
6310
|
async function setupPayments(config) {
|
|
6229
6311
|
const { payments, projectDir, frontend } = config;
|
|
6230
6312
|
if (!payments || payments === "none") return;
|
|
6231
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
6232
6313
|
const clientDir = path.join(projectDir, "apps/web");
|
|
6233
|
-
const
|
|
6314
|
+
const authDir = path.join(projectDir, "packages/auth");
|
|
6234
6315
|
const clientDirExists = await fs.pathExists(clientDir);
|
|
6235
|
-
|
|
6316
|
+
const authDirExists = await fs.pathExists(authDir);
|
|
6236
6317
|
if (payments === "polar") {
|
|
6237
|
-
await addPackageDependency({
|
|
6318
|
+
if (authDirExists) await addPackageDependency({
|
|
6238
6319
|
dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
|
|
6239
|
-
projectDir:
|
|
6320
|
+
projectDir: authDir
|
|
6240
6321
|
});
|
|
6241
6322
|
if (clientDirExists) {
|
|
6242
6323
|
if (frontend.some((f) => [
|
|
@@ -6310,19 +6391,20 @@ async function getDockerStatus(database) {
|
|
|
6310
6391
|
async function displayPostInstallInstructions(config) {
|
|
6311
6392
|
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
6312
6393
|
const isConvex = backend === "convex";
|
|
6394
|
+
const isBackendSelf = backend === "self";
|
|
6313
6395
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
6314
6396
|
const cdCmd = `cd ${relativePath}`;
|
|
6315
6397
|
const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
|
|
6316
|
-
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
|
|
6398
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6317
6399
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6318
6400
|
const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
|
|
6319
6401
|
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
|
|
6320
6402
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6321
6403
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6322
6404
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
6323
|
-
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
|
|
6324
|
-
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6325
|
-
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
6405
|
+
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
6406
|
+
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6407
|
+
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6326
6408
|
const hasWeb = frontend?.some((f) => [
|
|
6327
6409
|
"tanstack-router",
|
|
6328
6410
|
"react-router",
|
|
@@ -6346,7 +6428,8 @@ async function displayPostInstallInstructions(config) {
|
|
|
6346
6428
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
6347
6429
|
output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
|
|
6348
6430
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
6349
|
-
} else {
|
|
6431
|
+
} else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6432
|
+
else {
|
|
6350
6433
|
if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6351
6434
|
if (runtime === "workers") {
|
|
6352
6435
|
if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
|
|
@@ -6357,11 +6440,11 @@ async function displayPostInstallInstructions(config) {
|
|
|
6357
6440
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
6358
6441
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
6359
6442
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
6360
|
-
if (!isConvex) {
|
|
6443
|
+
if (!isConvex && !isBackendSelf) {
|
|
6361
6444
|
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6362
|
-
if (api === "orpc")
|
|
6363
|
-
else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6445
|
+
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6364
6446
|
}
|
|
6447
|
+
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
|
|
6365
6448
|
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
6366
6449
|
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
6367
6450
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
@@ -6393,7 +6476,7 @@ function getNativeInstructions(isConvex) {
|
|
|
6393
6476
|
function getLintingInstructions(runCmd) {
|
|
6394
6477
|
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
|
|
6395
6478
|
}
|
|
6396
|
-
async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
|
|
6479
|
+
async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
|
|
6397
6480
|
const instructions = [];
|
|
6398
6481
|
if (dbSetup === "docker") {
|
|
6399
6482
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -6407,8 +6490,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
|
|
|
6407
6490
|
const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
|
|
6408
6491
|
instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
|
|
6409
6492
|
instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
|
|
6410
|
-
|
|
6411
|
-
instructions.push(`${pc.cyan("
|
|
6493
|
+
const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
|
|
6494
|
+
instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
|
|
6495
|
+
instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
|
|
6412
6496
|
instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
|
|
6413
6497
|
instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
|
|
6414
6498
|
}
|
|
@@ -6432,7 +6516,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
|
|
|
6432
6516
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6433
6517
|
if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6434
6518
|
if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6435
|
-
if (database === "sqlite" && dbSetup !== "d1")
|
|
6519
|
+
if (database === "sqlite" && dbSetup !== "d1") {
|
|
6520
|
+
const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
|
|
6521
|
+
instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
|
|
6522
|
+
}
|
|
6436
6523
|
} else if (orm === "mongoose") {
|
|
6437
6524
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6438
6525
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
@@ -6453,23 +6540,28 @@ function getNoOrmWarning() {
|
|
|
6453
6540
|
function getBunWebNativeWarning() {
|
|
6454
6541
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
6455
6542
|
}
|
|
6456
|
-
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
6543
|
+
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6457
6544
|
const instructions = [];
|
|
6458
|
-
if (webDeploy === "wrangler")
|
|
6459
|
-
|
|
6545
|
+
if (webDeploy === "wrangler") {
|
|
6546
|
+
const deployPath = backend === "self" ? "apps/web" : "apps/web";
|
|
6547
|
+
instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
|
|
6548
|
+
}
|
|
6549
|
+
if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
|
|
6460
6550
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6461
6551
|
}
|
|
6462
6552
|
function getClerkInstructions() {
|
|
6463
6553
|
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`;
|
|
6464
6554
|
}
|
|
6465
|
-
function getPolarInstructions() {
|
|
6466
|
-
|
|
6555
|
+
function getPolarInstructions(backend) {
|
|
6556
|
+
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
6557
|
+
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}`;
|
|
6467
6558
|
}
|
|
6468
|
-
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
6559
|
+
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6469
6560
|
const instructions = [];
|
|
6561
|
+
const isBackendSelf = backend === "self";
|
|
6470
6562
|
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`}`);
|
|
6471
|
-
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`}`);
|
|
6472
|
-
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`}`);
|
|
6563
|
+
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`}`);
|
|
6564
|
+
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`}`);
|
|
6473
6565
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6474
6566
|
}
|
|
6475
6567
|
|
|
@@ -6514,20 +6606,31 @@ async function setupWorkspaceDependencies(projectDir, options) {
|
|
|
6514
6606
|
},
|
|
6515
6607
|
projectDir: serverPackageDir
|
|
6516
6608
|
});
|
|
6517
|
-
const needsApiDependency = options.api && options.api !== "none";
|
|
6518
6609
|
const webPackageDir = path.join(projectDir, "apps/web");
|
|
6519
6610
|
if (await fs.pathExists(webPackageDir)) {
|
|
6520
6611
|
const webDeps = {};
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
webDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6525
|
-
} else if (needsApiDependency) webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6612
|
+
webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6613
|
+
webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6614
|
+
webDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6526
6615
|
if (Object.keys(webDeps).length > 0) await addPackageDependency({
|
|
6527
6616
|
customDependencies: webDeps,
|
|
6528
6617
|
projectDir: webPackageDir
|
|
6529
6618
|
});
|
|
6530
6619
|
}
|
|
6620
|
+
const runtimeDevDeps = getRuntimeDevDeps(options);
|
|
6621
|
+
await addPackageDependency({
|
|
6622
|
+
dependencies: commonDeps,
|
|
6623
|
+
devDependencies: [...commonDevDeps, ...runtimeDevDeps],
|
|
6624
|
+
projectDir
|
|
6625
|
+
});
|
|
6626
|
+
}
|
|
6627
|
+
function getRuntimeDevDeps(options) {
|
|
6628
|
+
const { runtime, backend } = options;
|
|
6629
|
+
if (runtime === "none" && backend === "self") return ["@types/node"];
|
|
6630
|
+
if (runtime === "node") return ["@types/node"];
|
|
6631
|
+
if (runtime === "bun") return ["@types/bun"];
|
|
6632
|
+
if (runtime === "workers") return ["@types/node"];
|
|
6633
|
+
return [];
|
|
6531
6634
|
}
|
|
6532
6635
|
|
|
6533
6636
|
//#endregion
|
|
@@ -6536,6 +6639,7 @@ async function updatePackageConfigurations(projectDir, options) {
|
|
|
6536
6639
|
await updateRootPackageJson(projectDir, options);
|
|
6537
6640
|
if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
|
|
6538
6641
|
else if (options.backend === "self") {
|
|
6642
|
+
await updateDbPackageJson(projectDir, options);
|
|
6539
6643
|
await updateAuthPackageJson(projectDir, options);
|
|
6540
6644
|
await updateApiPackageJson(projectDir, options);
|
|
6541
6645
|
await setupWorkspaceDependencies(projectDir, options);
|
|
@@ -6585,10 +6689,10 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6585
6689
|
}
|
|
6586
6690
|
}
|
|
6587
6691
|
if (options.dbSetup === "docker") {
|
|
6588
|
-
scripts["db:start"] = `turbo -F ${
|
|
6589
|
-
scripts["db:watch"] = `turbo -F ${
|
|
6590
|
-
scripts["db:stop"] = `turbo -F ${
|
|
6591
|
-
scripts["db:down"] = `turbo -F ${
|
|
6692
|
+
scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
|
|
6693
|
+
scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
|
|
6694
|
+
scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
|
|
6695
|
+
scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
|
|
6592
6696
|
}
|
|
6593
6697
|
} else if (options.packageManager === "pnpm") {
|
|
6594
6698
|
scripts.dev = devScript;
|
|
@@ -6610,10 +6714,10 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6610
6714
|
}
|
|
6611
6715
|
}
|
|
6612
6716
|
if (options.dbSetup === "docker") {
|
|
6613
|
-
scripts["db:start"] = `pnpm --filter ${
|
|
6614
|
-
scripts["db:watch"] = `pnpm --filter ${
|
|
6615
|
-
scripts["db:stop"] = `pnpm --filter ${
|
|
6616
|
-
scripts["db:down"] = `pnpm --filter ${
|
|
6717
|
+
scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
|
|
6718
|
+
scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
|
|
6719
|
+
scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
|
|
6720
|
+
scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
|
|
6617
6721
|
}
|
|
6618
6722
|
} else if (options.packageManager === "npm") {
|
|
6619
6723
|
scripts.dev = devScript;
|
|
@@ -6635,10 +6739,10 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6635
6739
|
}
|
|
6636
6740
|
}
|
|
6637
6741
|
if (options.dbSetup === "docker") {
|
|
6638
|
-
scripts["db:start"] = `npm run db:start --workspace ${
|
|
6639
|
-
scripts["db:watch"] = `npm run db:watch --workspace ${
|
|
6640
|
-
scripts["db:stop"] = `npm run db:stop --workspace ${
|
|
6641
|
-
scripts["db:down"] = `npm run db:down --workspace ${
|
|
6742
|
+
scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
|
|
6743
|
+
scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
|
|
6744
|
+
scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
|
|
6745
|
+
scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
|
|
6642
6746
|
}
|
|
6643
6747
|
} else if (options.packageManager === "bun") {
|
|
6644
6748
|
scripts.dev = devScript;
|
|
@@ -6660,10 +6764,10 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6660
6764
|
}
|
|
6661
6765
|
}
|
|
6662
6766
|
if (options.dbSetup === "docker") {
|
|
6663
|
-
scripts["db:start"] = `bun run --filter ${
|
|
6664
|
-
scripts["db:watch"] = `bun run --filter ${
|
|
6665
|
-
scripts["db:stop"] = `bun run --filter ${
|
|
6666
|
-
scripts["db:down"] = `bun run --filter ${
|
|
6767
|
+
scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
|
|
6768
|
+
scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
|
|
6769
|
+
scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
|
|
6770
|
+
scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
|
|
6667
6771
|
}
|
|
6668
6772
|
}
|
|
6669
6773
|
try {
|
|
@@ -6688,13 +6792,6 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6688
6792
|
if (!await fs.pathExists(serverPackageJsonPath)) return;
|
|
6689
6793
|
const serverPackageJson = await fs.readJson(serverPackageJsonPath);
|
|
6690
6794
|
if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
|
|
6691
|
-
const scripts = serverPackageJson.scripts;
|
|
6692
|
-
if (options.dbSetup === "docker") {
|
|
6693
|
-
scripts["db:start"] = "docker compose up -d";
|
|
6694
|
-
scripts["db:watch"] = "docker compose up";
|
|
6695
|
-
scripts["db:stop"] = "docker compose stop";
|
|
6696
|
-
scripts["db:down"] = "docker compose down";
|
|
6697
|
-
}
|
|
6698
6795
|
await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
|
|
6699
6796
|
await updateDbPackageJson(projectDir, options);
|
|
6700
6797
|
}
|
|
@@ -6719,6 +6816,12 @@ async function updateDbPackageJson(projectDir, options) {
|
|
|
6719
6816
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "drizzle-kit migrate";
|
|
6720
6817
|
}
|
|
6721
6818
|
}
|
|
6819
|
+
if (options.dbSetup === "docker") {
|
|
6820
|
+
scripts["db:start"] = "docker compose up -d";
|
|
6821
|
+
scripts["db:watch"] = "docker compose up";
|
|
6822
|
+
scripts["db:stop"] = "docker compose stop";
|
|
6823
|
+
scripts["db:down"] = "docker compose down";
|
|
6824
|
+
}
|
|
6722
6825
|
await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
|
|
6723
6826
|
}
|
|
6724
6827
|
async function updateAuthPackageJson(projectDir, options) {
|