create-better-t-stack 3.4.0 → 3.4.2-canary.39d948b7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.js +1 -1
- package/dist/{src-oHcpd_LW.js → src-BEzWAB2k.js} +58 -438
- package/package.json +1 -1
- package/templates/api/orpc/server/tsconfig.json.hbs +1 -4
- package/templates/api/trpc/server/tsconfig.json.hbs +1 -4
- package/templates/auth/better-auth/server/base/tsconfig.json.hbs +1 -4
- package/templates/db/base/tsconfig.json.hbs +1 -4
- package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +0 -7
- package/templates/db/prisma/postgres/src/index.ts.hbs +0 -5
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +67 -5
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +2 -2
- package/templates/extras/_npmrc.hbs +2 -2
- package/templates/frontend/react/next/next.config.ts.hbs +5 -2
- package/templates/frontend/react/next/tsconfig.json.hbs +0 -3
- package/templates/frontend/react/tanstack-start/package.json.hbs +4 -4
- package/templates/packages/config/tsconfig.base.json.hbs +1 -1
- package/templates/deploy/wrangler/server/wrangler.jsonc.hbs +0 -39
- package/templates/deploy/wrangler/web/nuxt/wrangler.jsonc.hbs +0 -51
- package/templates/deploy/wrangler/web/react/next/open-next.config.ts +0 -6
- package/templates/deploy/wrangler/web/react/next/wrangler.jsonc.hbs +0 -22
- package/templates/deploy/wrangler/web/react/react-router/wrangler.jsonc.hbs +0 -8
- package/templates/deploy/wrangler/web/react/tanstack-router/wrangler.jsonc.hbs +0 -8
- package/templates/deploy/wrangler/web/react/tanstack-start/wrangler.jsonc.hbs +0 -20
- package/templates/deploy/wrangler/web/solid/wrangler.jsonc.hbs +0 -8
- package/templates/deploy/wrangler/web/svelte/wrangler.jsonc.hbs +0 -51
|
@@ -72,6 +72,7 @@ const dependencyVersionMap = {
|
|
|
72
72
|
"drizzle-kit": "^0.31.2",
|
|
73
73
|
"@planetscale/database": "^1.19.0",
|
|
74
74
|
"@libsql/client": "^0.14.0",
|
|
75
|
+
libsql: "^0.5.22",
|
|
75
76
|
"@neondatabase/serverless": "^1.0.1",
|
|
76
77
|
pg: "^8.14.1",
|
|
77
78
|
"@types/pg": "^8.11.11",
|
|
@@ -81,7 +82,6 @@ const dependencyVersionMap = {
|
|
|
81
82
|
"@prisma/client": "^6.15.0",
|
|
82
83
|
prisma: "^6.15.0",
|
|
83
84
|
"@prisma/adapter-d1": "^6.15.0",
|
|
84
|
-
"@prisma/extension-accelerate": "^2.0.2",
|
|
85
85
|
"@prisma/adapter-libsql": "^6.15.0",
|
|
86
86
|
"@prisma/adapter-planetscale": "^6.15.0",
|
|
87
87
|
mongoose: "^8.14.0",
|
|
@@ -279,16 +279,8 @@ const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(
|
|
|
279
279
|
"*"
|
|
280
280
|
].some((char) => name.includes(char));
|
|
281
281
|
}, "Project name contains invalid characters").refine((name) => name.toLowerCase() !== "node_modules", "Project name is reserved").describe("Project name or path");
|
|
282
|
-
const WebDeploySchema = z.enum([
|
|
283
|
-
|
|
284
|
-
"alchemy",
|
|
285
|
-
"none"
|
|
286
|
-
]).describe("Web deployment");
|
|
287
|
-
const ServerDeploySchema = z.enum([
|
|
288
|
-
"wrangler",
|
|
289
|
-
"alchemy",
|
|
290
|
-
"none"
|
|
291
|
-
]).describe("Server deployment");
|
|
282
|
+
const WebDeploySchema = z.enum(["alchemy", "none"]).describe("Web deployment");
|
|
283
|
+
const ServerDeploySchema = z.enum(["alchemy", "none"]).describe("Server deployment");
|
|
292
284
|
const DirectoryConflictSchema = z.enum([
|
|
293
285
|
"merge",
|
|
294
286
|
"overwrite",
|
|
@@ -977,7 +969,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
977
969
|
{
|
|
978
970
|
value: "native-uniwind",
|
|
979
971
|
label: "Uniwind",
|
|
980
|
-
hint: "
|
|
972
|
+
hint: "Fastest Tailwind bindings for React Native with HeroUI Native"
|
|
981
973
|
},
|
|
982
974
|
{
|
|
983
975
|
value: "native-unistyles",
|
|
@@ -1132,10 +1124,6 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
1132
1124
|
//#endregion
|
|
1133
1125
|
//#region src/prompts/server-deploy.ts
|
|
1134
1126
|
function getDeploymentDisplay$1(deployment) {
|
|
1135
|
-
if (deployment === "wrangler") return {
|
|
1136
|
-
label: "Wrangler",
|
|
1137
|
-
hint: "Deploy to Cloudflare Workers using Wrangler"
|
|
1138
|
-
};
|
|
1139
1127
|
if (deployment === "alchemy") return {
|
|
1140
1128
|
label: "Alchemy",
|
|
1141
1129
|
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
@@ -1145,40 +1133,17 @@ function getDeploymentDisplay$1(deployment) {
|
|
|
1145
1133
|
hint: `Add ${deployment} deployment`
|
|
1146
1134
|
};
|
|
1147
1135
|
}
|
|
1148
|
-
async function getServerDeploymentChoice(deployment, runtime, backend,
|
|
1136
|
+
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
1149
1137
|
if (deployment !== void 0) return deployment;
|
|
1150
1138
|
if (backend === "none" || backend === "convex") return "none";
|
|
1151
1139
|
if (backend !== "hono") return "none";
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
["alchemy", "wrangler"].forEach((deploy) => {
|
|
1155
|
-
const { label, hint } = getDeploymentDisplay$1(deploy);
|
|
1156
|
-
options.unshift({
|
|
1157
|
-
value: deploy,
|
|
1158
|
-
label,
|
|
1159
|
-
hint
|
|
1160
|
-
});
|
|
1161
|
-
});
|
|
1162
|
-
const response = await select({
|
|
1163
|
-
message: "Select server deployment",
|
|
1164
|
-
options,
|
|
1165
|
-
initialValue: webDeploy === "alchemy" ? "alchemy" : runtime === "workers" ? "wrangler" : DEFAULT_CONFIG.serverDeploy
|
|
1166
|
-
});
|
|
1167
|
-
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1168
|
-
return response;
|
|
1140
|
+
if (runtime === "workers") return "alchemy";
|
|
1141
|
+
return "none";
|
|
1169
1142
|
}
|
|
1170
1143
|
async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
1171
1144
|
if (backend !== "hono") return "none";
|
|
1172
1145
|
const options = [];
|
|
1173
1146
|
if (runtime === "workers") {
|
|
1174
|
-
if (existingDeployment !== "wrangler") {
|
|
1175
|
-
const { label, hint } = getDeploymentDisplay$1("wrangler");
|
|
1176
|
-
options.push({
|
|
1177
|
-
value: "wrangler",
|
|
1178
|
-
label,
|
|
1179
|
-
hint
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
1147
|
if (existingDeployment !== "alchemy") {
|
|
1183
1148
|
const { label, hint } = getDeploymentDisplay$1("alchemy");
|
|
1184
1149
|
options.push({
|
|
@@ -1194,7 +1159,7 @@ async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
|
1194
1159
|
const response = await select({
|
|
1195
1160
|
message: "Select server deployment",
|
|
1196
1161
|
options,
|
|
1197
|
-
initialValue:
|
|
1162
|
+
initialValue: DEFAULT_CONFIG.serverDeploy
|
|
1198
1163
|
});
|
|
1199
1164
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1200
1165
|
return response;
|
|
@@ -1206,10 +1171,6 @@ function hasWebFrontend(frontends) {
|
|
|
1206
1171
|
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
1207
1172
|
}
|
|
1208
1173
|
function getDeploymentDisplay(deployment) {
|
|
1209
|
-
if (deployment === "wrangler") return {
|
|
1210
|
-
label: "Wrangler",
|
|
1211
|
-
hint: "Deploy to Cloudflare Workers using Wrangler"
|
|
1212
|
-
};
|
|
1213
1174
|
if (deployment === "alchemy") return {
|
|
1214
1175
|
label: "Alchemy",
|
|
1215
1176
|
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
@@ -1224,11 +1185,7 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1224
1185
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1225
1186
|
const response = await select({
|
|
1226
1187
|
message: "Select web deployment",
|
|
1227
|
-
options: [
|
|
1228
|
-
"wrangler",
|
|
1229
|
-
"alchemy",
|
|
1230
|
-
"none"
|
|
1231
|
-
].map((deploy) => {
|
|
1188
|
+
options: ["alchemy", "none"].map((deploy) => {
|
|
1232
1189
|
const { label, hint } = getDeploymentDisplay(deploy);
|
|
1233
1190
|
return {
|
|
1234
1191
|
value: deploy,
|
|
@@ -1244,14 +1201,6 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1244
1201
|
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
1245
1202
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1246
1203
|
const options = [];
|
|
1247
|
-
if (existingDeployment !== "wrangler") {
|
|
1248
|
-
const { label, hint } = getDeploymentDisplay("wrangler");
|
|
1249
|
-
options.push({
|
|
1250
|
-
value: "wrangler",
|
|
1251
|
-
label,
|
|
1252
|
-
hint
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
1204
|
if (existingDeployment !== "alchemy") {
|
|
1256
1205
|
const { label, hint } = getDeploymentDisplay("alchemy");
|
|
1257
1206
|
options.push({
|
|
@@ -1387,7 +1336,7 @@ const getLatestCLIVersion = () => {
|
|
|
1387
1336
|
*/
|
|
1388
1337
|
function isTelemetryEnabled() {
|
|
1389
1338
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1390
|
-
const BTS_TELEMETRY = "
|
|
1339
|
+
const BTS_TELEMETRY = "0";
|
|
1391
1340
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1392
1341
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1393
1342
|
return true;
|
|
@@ -1395,8 +1344,8 @@ function isTelemetryEnabled() {
|
|
|
1395
1344
|
|
|
1396
1345
|
//#endregion
|
|
1397
1346
|
//#region src/utils/analytics.ts
|
|
1398
|
-
const POSTHOG_API_KEY = "
|
|
1399
|
-
const POSTHOG_HOST = "
|
|
1347
|
+
const POSTHOG_API_KEY = "random";
|
|
1348
|
+
const POSTHOG_HOST = "random";
|
|
1400
1349
|
function generateSessionId() {
|
|
1401
1350
|
const rand = Math.random().toString(36).slice(2);
|
|
1402
1351
|
return `cli_${Date.now().toString(36)}${rand}`;
|
|
@@ -1815,8 +1764,8 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1815
1764
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
1816
1765
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
1817
1766
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
1818
|
-
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose '
|
|
1819
|
-
if (providedFlags.has("serverDeploy") &&
|
|
1767
|
+
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
|
|
1768
|
+
if (providedFlags.has("serverDeploy") && config.serverDeploy === "alchemy" && config.runtime !== "workers") exitWithError(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
|
|
1820
1769
|
if (config.addons && config.addons.length > 0) {
|
|
1821
1770
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1822
1771
|
config.addons = [...new Set(config.addons)];
|
|
@@ -3227,14 +3176,14 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
3227
3176
|
if (await fs.pathExists(alchemyTemplateSrc)) {
|
|
3228
3177
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3229
3178
|
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, isBackendSelf && await fs.pathExists(webAppDir) ? webAppDir : projectDir, context);
|
|
3230
|
-
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3179
|
+
if (!isBackendSelf) await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3231
3180
|
}
|
|
3232
3181
|
} else {
|
|
3233
3182
|
if (context.webDeploy === "alchemy") {
|
|
3234
3183
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3235
3184
|
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
|
|
3236
3185
|
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
|
|
3237
|
-
await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3186
|
+
if (!isBackendSelf) await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
|
|
3238
3187
|
}
|
|
3239
3188
|
}
|
|
3240
3189
|
if (context.serverDeploy === "alchemy" && !isBackendSelf) {
|
|
@@ -3338,54 +3287,13 @@ async function addAddonsToProject(input) {
|
|
|
3338
3287
|
//#region src/helpers/deployment/server-deploy-setup.ts
|
|
3339
3288
|
async function setupServerDeploy(config) {
|
|
3340
3289
|
const { serverDeploy, webDeploy, projectDir } = config;
|
|
3341
|
-
const { packageManager } = config;
|
|
3342
3290
|
if (serverDeploy === "none") return;
|
|
3343
3291
|
if (serverDeploy === "alchemy" && webDeploy === "alchemy") return;
|
|
3344
3292
|
const serverDir = path.join(projectDir, "apps/server");
|
|
3345
3293
|
if (!await fs.pathExists(serverDir)) return;
|
|
3346
|
-
if (serverDeploy === "
|
|
3347
|
-
await setupWorkersServerDeploy(serverDir, packageManager);
|
|
3348
|
-
await generateCloudflareWorkerTypes({
|
|
3349
|
-
serverDir,
|
|
3350
|
-
packageManager
|
|
3351
|
-
});
|
|
3352
|
-
} else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
|
|
3294
|
+
if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
3353
3295
|
}
|
|
3354
|
-
async function
|
|
3355
|
-
const packageJsonPath = path.join(serverDir, "package.json");
|
|
3356
|
-
if (!await fs.pathExists(packageJsonPath)) return;
|
|
3357
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3358
|
-
packageJson.scripts = {
|
|
3359
|
-
...packageJson.scripts,
|
|
3360
|
-
dev: "wrangler dev --port=3000",
|
|
3361
|
-
start: "wrangler dev",
|
|
3362
|
-
deploy: "wrangler deploy",
|
|
3363
|
-
build: "wrangler deploy --dry-run",
|
|
3364
|
-
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
|
|
3365
|
-
};
|
|
3366
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3367
|
-
await addPackageDependency({
|
|
3368
|
-
devDependencies: ["wrangler", "@types/node"],
|
|
3369
|
-
projectDir: serverDir
|
|
3370
|
-
});
|
|
3371
|
-
}
|
|
3372
|
-
async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
|
|
3373
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
3374
|
-
const s = spinner();
|
|
3375
|
-
try {
|
|
3376
|
-
s.start("Generating Cloudflare Workers types...");
|
|
3377
|
-
await execa(getPackageExecutionCommand(packageManager, "wrangler types --env-interface CloudflareBindings"), {
|
|
3378
|
-
cwd: serverDir,
|
|
3379
|
-
shell: true
|
|
3380
|
-
});
|
|
3381
|
-
s.stop("Cloudflare Workers types generated successfully!");
|
|
3382
|
-
} catch {
|
|
3383
|
-
s.stop(pc.yellow("Failed to generate Cloudflare Workers types"));
|
|
3384
|
-
const managerCmd = `${packageManager} run`;
|
|
3385
|
-
log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
|
|
3386
|
-
}
|
|
3387
|
-
}
|
|
3388
|
-
async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
|
|
3296
|
+
async function setupAlchemyServerDeploy(serverDir, projectDir) {
|
|
3389
3297
|
if (!await fs.pathExists(serverDir)) return;
|
|
3390
3298
|
await addPackageDependency({
|
|
3391
3299
|
devDependencies: [
|
|
@@ -3720,7 +3628,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3720
3628
|
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3721
3629
|
}
|
|
3722
3630
|
const serverDir = path.join(projectDir, "apps/server");
|
|
3723
|
-
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir,
|
|
3631
|
+
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
3724
3632
|
const frontend = config.frontend;
|
|
3725
3633
|
const isNext = frontend.includes("next");
|
|
3726
3634
|
const isNuxt = frontend.includes("nuxt");
|
|
@@ -3738,219 +3646,13 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3738
3646
|
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3739
3647
|
}
|
|
3740
3648
|
|
|
3741
|
-
//#endregion
|
|
3742
|
-
//#region src/helpers/deployment/workers/workers-next-setup.ts
|
|
3743
|
-
async function setupNextWorkersDeploy(projectDir, _packageManager) {
|
|
3744
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3745
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3746
|
-
await addPackageDependency({
|
|
3747
|
-
dependencies: ["@opennextjs/cloudflare"],
|
|
3748
|
-
devDependencies: ["wrangler"],
|
|
3749
|
-
projectDir: webAppDir
|
|
3750
|
-
});
|
|
3751
|
-
const packageJsonPath = path.join(webAppDir, "package.json");
|
|
3752
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3753
|
-
const pkg = await fs.readJson(packageJsonPath);
|
|
3754
|
-
pkg.scripts = {
|
|
3755
|
-
...pkg.scripts,
|
|
3756
|
-
preview: "opennextjs-cloudflare build && opennextjs-cloudflare preview",
|
|
3757
|
-
deploy: "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
|
|
3758
|
-
upload: "opennextjs-cloudflare build && opennextjs-cloudflare upload",
|
|
3759
|
-
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
|
|
3760
|
-
};
|
|
3761
|
-
await fs.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
3762
|
-
}
|
|
3763
|
-
}
|
|
3764
|
-
|
|
3765
|
-
//#endregion
|
|
3766
|
-
//#region src/helpers/deployment/workers/workers-nuxt-setup.ts
|
|
3767
|
-
async function setupNuxtWorkersDeploy(projectDir, packageManager) {
|
|
3768
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3769
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3770
|
-
await addPackageDependency({
|
|
3771
|
-
devDependencies: ["nitro-cloudflare-dev", "wrangler"],
|
|
3772
|
-
projectDir: webAppDir
|
|
3773
|
-
});
|
|
3774
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3775
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3776
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3777
|
-
pkg.scripts = {
|
|
3778
|
-
...pkg.scripts,
|
|
3779
|
-
deploy: `${packageManager} run build && wrangler deploy`,
|
|
3780
|
-
"cf-typegen": "wrangler types"
|
|
3781
|
-
};
|
|
3782
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3783
|
-
}
|
|
3784
|
-
const nuxtConfigPath = path.join(webAppDir, "nuxt.config.ts");
|
|
3785
|
-
if (!await fs.pathExists(nuxtConfigPath)) return;
|
|
3786
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(nuxtConfigPath);
|
|
3787
|
-
if (!sourceFile) return;
|
|
3788
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
3789
|
-
const expression = expr.getExpression();
|
|
3790
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineNuxtConfig";
|
|
3791
|
-
});
|
|
3792
|
-
if (!defineCall) return;
|
|
3793
|
-
const configObj = defineCall.getArguments()[0];
|
|
3794
|
-
if (!configObj) return;
|
|
3795
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3796
|
-
const compatProp = configObj.getProperty("compatibilityDate");
|
|
3797
|
-
if (compatProp && compatProp.getKind() === SyntaxKind.PropertyAssignment) compatProp.setInitializer(`'${today}'`);
|
|
3798
|
-
else configObj.addPropertyAssignment({
|
|
3799
|
-
name: "compatibilityDate",
|
|
3800
|
-
initializer: `'${today}'`
|
|
3801
|
-
});
|
|
3802
|
-
const nitroInitializer = `{
|
|
3803
|
-
preset: "cloudflare_module",
|
|
3804
|
-
cloudflare: {
|
|
3805
|
-
deployConfig: true,
|
|
3806
|
-
nodeCompat: true
|
|
3807
|
-
}
|
|
3808
|
-
}`;
|
|
3809
|
-
const nitroProp = configObj.getProperty("nitro");
|
|
3810
|
-
if (nitroProp && nitroProp.getKind() === SyntaxKind.PropertyAssignment) nitroProp.setInitializer(nitroInitializer);
|
|
3811
|
-
else configObj.addPropertyAssignment({
|
|
3812
|
-
name: "nitro",
|
|
3813
|
-
initializer: nitroInitializer
|
|
3814
|
-
});
|
|
3815
|
-
const modulesProp = configObj.getProperty("modules");
|
|
3816
|
-
if (modulesProp && modulesProp.getKind() === SyntaxKind.PropertyAssignment) {
|
|
3817
|
-
const arrayExpr = modulesProp.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression);
|
|
3818
|
-
if (arrayExpr) {
|
|
3819
|
-
if (!arrayExpr.getElements().some((el) => el.getText().replace(/['"`]/g, "") === "nitro-cloudflare-dev")) arrayExpr.addElement("'nitro-cloudflare-dev'");
|
|
3820
|
-
}
|
|
3821
|
-
} else configObj.addPropertyAssignment({
|
|
3822
|
-
name: "modules",
|
|
3823
|
-
initializer: "['nitro-cloudflare-dev']"
|
|
3824
|
-
});
|
|
3825
|
-
await tsProject.save();
|
|
3826
|
-
}
|
|
3827
|
-
|
|
3828
|
-
//#endregion
|
|
3829
|
-
//#region src/helpers/deployment/workers/workers-svelte-setup.ts
|
|
3830
|
-
async function setupSvelteWorkersDeploy(projectDir, packageManager) {
|
|
3831
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3832
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3833
|
-
await addPackageDependency({
|
|
3834
|
-
devDependencies: ["@sveltejs/adapter-cloudflare", "wrangler"],
|
|
3835
|
-
projectDir: webAppDir
|
|
3836
|
-
});
|
|
3837
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3838
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3839
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3840
|
-
pkg.scripts = {
|
|
3841
|
-
...pkg.scripts,
|
|
3842
|
-
deploy: `${packageManager} run build && wrangler deploy`,
|
|
3843
|
-
"cf-typegen": "wrangler types ./src/worker-configuration.d.ts"
|
|
3844
|
-
};
|
|
3845
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3846
|
-
}
|
|
3847
|
-
const possibleConfigFiles = [path.join(webAppDir, "svelte.config.js"), path.join(webAppDir, "svelte.config.ts")];
|
|
3848
|
-
const existingConfigPath = (await Promise.all(possibleConfigFiles.map(async (p) => await fs.pathExists(p) ? p : ""))).find((p) => p);
|
|
3849
|
-
if (existingConfigPath) {
|
|
3850
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(existingConfigPath);
|
|
3851
|
-
if (!sourceFile) return;
|
|
3852
|
-
const adapterImport = sourceFile.getImportDeclarations().find((imp) => ["@sveltejs/adapter-auto", "@sveltejs/adapter-node"].includes(imp.getModuleSpecifierValue()));
|
|
3853
|
-
if (adapterImport) adapterImport.setModuleSpecifier("@sveltejs/adapter-cloudflare");
|
|
3854
|
-
else if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@sveltejs/adapter-cloudflare")) sourceFile.insertImportDeclaration(0, {
|
|
3855
|
-
defaultImport: "adapter",
|
|
3856
|
-
moduleSpecifier: "@sveltejs/adapter-cloudflare"
|
|
3857
|
-
});
|
|
3858
|
-
await tsProject.save();
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
|
|
3862
|
-
//#endregion
|
|
3863
|
-
//#region src/helpers/deployment/workers/workers-tanstack-start-setup.ts
|
|
3864
|
-
async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
|
|
3865
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3866
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3867
|
-
await addPackageDependency({
|
|
3868
|
-
devDependencies: ["wrangler", "@cloudflare/vite-plugin"],
|
|
3869
|
-
projectDir: webAppDir
|
|
3870
|
-
});
|
|
3871
|
-
const pkgPath = path.join(webAppDir, "package.json");
|
|
3872
|
-
if (await fs.pathExists(pkgPath)) {
|
|
3873
|
-
const pkg = await fs.readJson(pkgPath);
|
|
3874
|
-
pkg.scripts = {
|
|
3875
|
-
...pkg.scripts,
|
|
3876
|
-
deploy: `${packageManager} run build && wrangler deploy`,
|
|
3877
|
-
"cf-typegen": "wrangler types --env-interface Env"
|
|
3878
|
-
};
|
|
3879
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3880
|
-
}
|
|
3881
|
-
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
3882
|
-
if (!await fs.pathExists(viteConfigPath)) return;
|
|
3883
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
3884
|
-
if (!sourceFile) return;
|
|
3885
|
-
const cfImport = sourceFile.getImportDeclaration("@cloudflare/vite-plugin");
|
|
3886
|
-
if (!cfImport) sourceFile.addImportDeclaration({
|
|
3887
|
-
moduleSpecifier: "@cloudflare/vite-plugin",
|
|
3888
|
-
namedImports: [{ name: "cloudflare" }]
|
|
3889
|
-
});
|
|
3890
|
-
else if (!cfImport.getNamedImports().some((ni) => ni.getName() === "cloudflare")) cfImport.addNamedImport({ name: "cloudflare" });
|
|
3891
|
-
const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
|
|
3892
|
-
let reactPluginIdentifier = "viteReact";
|
|
3893
|
-
if (!reactImport) sourceFile.addImportDeclaration({
|
|
3894
|
-
moduleSpecifier: "@vitejs/plugin-react",
|
|
3895
|
-
defaultImport: "viteReact"
|
|
3896
|
-
});
|
|
3897
|
-
else {
|
|
3898
|
-
const defaultImport = reactImport.getDefaultImport();
|
|
3899
|
-
if (defaultImport) reactPluginIdentifier = defaultImport.getText();
|
|
3900
|
-
else reactImport.setDefaultImport("viteReact");
|
|
3901
|
-
}
|
|
3902
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
3903
|
-
const expression = expr.getExpression();
|
|
3904
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
3905
|
-
});
|
|
3906
|
-
if (!defineCall) return;
|
|
3907
|
-
const configObj = defineCall.getArguments()[0];
|
|
3908
|
-
if (!configObj) return;
|
|
3909
|
-
const pluginsArray = ensureArrayProperty(configObj, "plugins");
|
|
3910
|
-
if (!pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("))) pluginsArray.insertElement(0, "cloudflare({ viteEnvironment: { name: 'ssr' } })");
|
|
3911
|
-
if (!pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier)) {
|
|
3912
|
-
const nextIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart(")) + 1;
|
|
3913
|
-
if (nextIndex > 0) pluginsArray.insertElement(nextIndex, `${reactPluginIdentifier}()`);
|
|
3914
|
-
else pluginsArray.addElement(`${reactPluginIdentifier}()`);
|
|
3915
|
-
}
|
|
3916
|
-
await tsProject.save();
|
|
3917
|
-
}
|
|
3918
|
-
|
|
3919
|
-
//#endregion
|
|
3920
|
-
//#region src/helpers/deployment/workers/workers-vite-setup.ts
|
|
3921
|
-
async function setupWorkersVitePlugin(projectDir) {
|
|
3922
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3923
|
-
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
3924
|
-
if (!await fs.pathExists(viteConfigPath)) throw new Error("vite.config.ts not found in web app directory");
|
|
3925
|
-
await addPackageDependency({
|
|
3926
|
-
devDependencies: ["@cloudflare/vite-plugin", "wrangler"],
|
|
3927
|
-
projectDir: webAppDir
|
|
3928
|
-
});
|
|
3929
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
3930
|
-
if (!sourceFile) throw new Error("vite.config.ts not found in web app directory");
|
|
3931
|
-
if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@cloudflare/vite-plugin")) sourceFile.insertImportDeclaration(0, {
|
|
3932
|
-
namedImports: ["cloudflare"],
|
|
3933
|
-
moduleSpecifier: "@cloudflare/vite-plugin"
|
|
3934
|
-
});
|
|
3935
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
3936
|
-
const expression = expr.getExpression();
|
|
3937
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
3938
|
-
});
|
|
3939
|
-
if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
|
|
3940
|
-
const configObject = defineCall.getArguments()[0];
|
|
3941
|
-
if (!configObject) throw new Error("defineConfig argument is not an object literal");
|
|
3942
|
-
const pluginsArray = ensureArrayProperty(configObject, "plugins");
|
|
3943
|
-
if (!pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("))) pluginsArray.addElement("cloudflare()");
|
|
3944
|
-
await tsProject.save();
|
|
3945
|
-
}
|
|
3946
|
-
|
|
3947
3649
|
//#endregion
|
|
3948
3650
|
//#region src/helpers/deployment/web-deploy-setup.ts
|
|
3949
3651
|
async function setupWebDeploy(config) {
|
|
3950
3652
|
const { webDeploy, serverDeploy, frontend, projectDir } = config;
|
|
3951
3653
|
const { packageManager } = config;
|
|
3952
3654
|
if (webDeploy === "none") return;
|
|
3953
|
-
if (webDeploy !== "
|
|
3655
|
+
if (webDeploy !== "alchemy") return;
|
|
3954
3656
|
if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
|
|
3955
3657
|
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
3956
3658
|
await addAlchemyPackagesDependencies(projectDir);
|
|
@@ -3963,37 +3665,14 @@ async function setupWebDeploy(config) {
|
|
|
3963
3665
|
const isTanstackStart = frontend.includes("tanstack-start");
|
|
3964
3666
|
const isReactRouter = frontend.includes("react-router");
|
|
3965
3667
|
const isSolid = frontend.includes("solid");
|
|
3966
|
-
if (
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
|
|
3975
|
-
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
|
|
3976
|
-
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
|
|
3977
|
-
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
3978
|
-
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
3979
|
-
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
3980
|
-
await addAlchemyPackagesDependencies(projectDir);
|
|
3981
|
-
}
|
|
3982
|
-
}
|
|
3983
|
-
async function setupWorkersWebDeploy(projectDir, pkgManager) {
|
|
3984
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3985
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3986
|
-
const packageJsonPath = path.join(webAppDir, "package.json");
|
|
3987
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3988
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3989
|
-
packageJson.scripts = {
|
|
3990
|
-
...packageJson.scripts,
|
|
3991
|
-
"wrangler:dev": "wrangler dev --port=3001",
|
|
3992
|
-
deploy: `${pkgManager} run build && wrangler deploy`
|
|
3993
|
-
};
|
|
3994
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3995
|
-
}
|
|
3996
|
-
await setupWorkersVitePlugin(projectDir);
|
|
3668
|
+
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager);
|
|
3669
|
+
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
|
|
3670
|
+
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
|
|
3671
|
+
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
|
|
3672
|
+
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
3673
|
+
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
3674
|
+
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
3675
|
+
await addAlchemyPackagesDependencies(projectDir);
|
|
3997
3676
|
}
|
|
3998
3677
|
async function addAlchemyPackagesDependencies(projectDir) {
|
|
3999
3678
|
await addPackageDependency({
|
|
@@ -4854,7 +4533,7 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
|
|
|
4854
4533
|
databaseUrl = "mongodb://localhost:27017/mydatabase";
|
|
4855
4534
|
break;
|
|
4856
4535
|
case "sqlite":
|
|
4857
|
-
if (config.runtime === "workers" || webDeploy === "
|
|
4536
|
+
if (config.runtime === "workers" || webDeploy === "alchemy" || serverDeploy === "alchemy") databaseUrl = "http://127.0.0.1:8080";
|
|
4858
4537
|
else {
|
|
4859
4538
|
const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
|
|
4860
4539
|
databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
|
|
@@ -4936,31 +4615,7 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
|
|
|
4936
4615
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4937
4616
|
async function setupCloudflareD1(config) {
|
|
4938
4617
|
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4939
|
-
if (serverDeploy === "
|
|
4940
|
-
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
4941
|
-
const envPath = path.join(projectDir, targetApp, ".env");
|
|
4942
|
-
const variables = [
|
|
4943
|
-
{
|
|
4944
|
-
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
4945
|
-
value: "",
|
|
4946
|
-
condition: true
|
|
4947
|
-
},
|
|
4948
|
-
{
|
|
4949
|
-
key: "CLOUDFLARE_DATABASE_ID",
|
|
4950
|
-
value: "",
|
|
4951
|
-
condition: true
|
|
4952
|
-
},
|
|
4953
|
-
{
|
|
4954
|
-
key: "CLOUDFLARE_D1_TOKEN",
|
|
4955
|
-
value: "",
|
|
4956
|
-
condition: true
|
|
4957
|
-
}
|
|
4958
|
-
];
|
|
4959
|
-
try {
|
|
4960
|
-
await addEnvVariablesToFile(envPath, variables);
|
|
4961
|
-
} catch (_err) {}
|
|
4962
|
-
}
|
|
4963
|
-
if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
|
|
4618
|
+
if (serverDeploy === "alchemy" && orm === "prisma") {
|
|
4964
4619
|
const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
|
|
4965
4620
|
const envPath = path.join(projectDir, targetApp2, ".env");
|
|
4966
4621
|
const variables = [{
|
|
@@ -5407,7 +5062,7 @@ const AVAILABLE_REGIONS = [
|
|
|
5407
5062
|
label: "US West (N. California)"
|
|
5408
5063
|
}
|
|
5409
5064
|
];
|
|
5410
|
-
async function setupWithCreateDb(serverDir, packageManager
|
|
5065
|
+
async function setupWithCreateDb(serverDir, packageManager) {
|
|
5411
5066
|
try {
|
|
5412
5067
|
log.info("Starting Prisma Postgres setup with create-db.");
|
|
5413
5068
|
const selectedRegion = await select({
|
|
@@ -5432,7 +5087,7 @@ async function setupWithCreateDb(serverDir, packageManager, orm) {
|
|
|
5432
5087
|
return null;
|
|
5433
5088
|
}
|
|
5434
5089
|
return {
|
|
5435
|
-
databaseUrl:
|
|
5090
|
+
databaseUrl: createDbResponse.connectionString,
|
|
5436
5091
|
claimUrl: createDbResponse.claimUrl
|
|
5437
5092
|
};
|
|
5438
5093
|
} catch (error) {
|
|
@@ -5450,12 +5105,12 @@ async function initPrismaDatabase(serverDir, packageManager) {
|
|
|
5450
5105
|
stdio: "inherit",
|
|
5451
5106
|
shell: true
|
|
5452
5107
|
});
|
|
5453
|
-
log.info(pc.yellow("Please copy the Prisma Postgres URL
|
|
5108
|
+
log.info(pc.yellow("Please copy the Prisma Postgres URL.\nIt looks like: postgresql://user:password@host:5432/db?sslmode=require"));
|
|
5454
5109
|
const databaseUrl = await text({
|
|
5455
5110
|
message: "Paste your Prisma Postgres database URL:",
|
|
5456
5111
|
validate(value) {
|
|
5457
5112
|
if (!value) return "Please enter a database URL";
|
|
5458
|
-
if (!value.startsWith("
|
|
5113
|
+
if (!value.startsWith("postgresql://")) return "URL should start with postgresql://";
|
|
5459
5114
|
}
|
|
5460
5115
|
});
|
|
5461
5116
|
if (isCancel(databaseUrl)) return null;
|
|
@@ -5504,18 +5159,6 @@ function displayManualSetupInstructions$1(target) {
|
|
|
5504
5159
|
|
|
5505
5160
|
DATABASE_URL="your_database_url"`);
|
|
5506
5161
|
}
|
|
5507
|
-
async function addPrismaAccelerateExtension(projectDir) {
|
|
5508
|
-
try {
|
|
5509
|
-
await addPackageDependency({
|
|
5510
|
-
dependencies: ["@prisma/extension-accelerate"],
|
|
5511
|
-
projectDir: path.join(projectDir, "packages/db")
|
|
5512
|
-
});
|
|
5513
|
-
return true;
|
|
5514
|
-
} catch (_error) {
|
|
5515
|
-
log.warn(pc.yellow("Could not add Prisma Accelerate extension automatically"));
|
|
5516
|
-
return false;
|
|
5517
|
-
}
|
|
5518
|
-
}
|
|
5519
5162
|
async function setupPrismaPostgres(config, cliInput) {
|
|
5520
5163
|
const { packageManager, projectDir, orm, backend } = config;
|
|
5521
5164
|
const manualDb = cliInput?.manualDb ?? false;
|
|
@@ -5563,16 +5206,12 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5563
5206
|
});
|
|
5564
5207
|
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
5565
5208
|
let prismaConfig = null;
|
|
5566
|
-
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager
|
|
5209
|
+
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager);
|
|
5567
5210
|
else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
|
|
5568
5211
|
if (prismaConfig) {
|
|
5569
5212
|
await writeEnvFile$1(projectDir, backend, prismaConfig);
|
|
5570
|
-
if (orm === "prisma")
|
|
5571
|
-
|
|
5572
|
-
await addPrismaAccelerateExtension(projectDir);
|
|
5573
|
-
}
|
|
5574
|
-
const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
|
|
5575
|
-
log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
|
|
5213
|
+
if (orm === "prisma") await addDotenvImportToPrismaConfig(projectDir, backend);
|
|
5214
|
+
log.success(pc.green("Prisma Postgres database configured successfully!"));
|
|
5576
5215
|
if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
|
|
5577
5216
|
} else {
|
|
5578
5217
|
await writeEnvFile$1(projectDir, backend);
|
|
@@ -5968,6 +5607,7 @@ async function setupDatabase(config, cliInput) {
|
|
|
5968
5607
|
}
|
|
5969
5608
|
const s = spinner();
|
|
5970
5609
|
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5610
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
5971
5611
|
if (!await fs.pathExists(dbPackageDir)) return;
|
|
5972
5612
|
try {
|
|
5973
5613
|
if (orm === "prisma") {
|
|
@@ -5990,18 +5630,26 @@ async function setupDatabase(config, cliInput) {
|
|
|
5990
5630
|
devDependencies: ["prisma"],
|
|
5991
5631
|
projectDir: dbPackageDir
|
|
5992
5632
|
});
|
|
5993
|
-
const webDir = path.join(projectDir, "apps/web");
|
|
5994
5633
|
if (await fs.pathExists(webDir)) await addPackageDependency({
|
|
5995
5634
|
dependencies: ["@prisma/client"],
|
|
5996
5635
|
projectDir: webDir
|
|
5997
5636
|
});
|
|
5998
5637
|
} else if (orm === "drizzle") {
|
|
5999
|
-
if (database === "sqlite")
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
5638
|
+
if (database === "sqlite") {
|
|
5639
|
+
await addPackageDependency({
|
|
5640
|
+
dependencies: [
|
|
5641
|
+
"drizzle-orm",
|
|
5642
|
+
"@libsql/client",
|
|
5643
|
+
"libsql"
|
|
5644
|
+
],
|
|
5645
|
+
devDependencies: ["drizzle-kit"],
|
|
5646
|
+
projectDir: dbPackageDir
|
|
5647
|
+
});
|
|
5648
|
+
await addPackageDependency({
|
|
5649
|
+
dependencies: ["@libsql/client", "libsql"],
|
|
5650
|
+
projectDir: webDir
|
|
5651
|
+
});
|
|
5652
|
+
} else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
|
|
6005
5653
|
dependencies: [
|
|
6006
5654
|
"drizzle-orm",
|
|
6007
5655
|
"@neondatabase/serverless",
|
|
@@ -6320,7 +5968,7 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
6320
5968
|
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
6321
5969
|
return addonsList.join("\n");
|
|
6322
5970
|
}
|
|
6323
|
-
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup,
|
|
5971
|
+
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, _serverDeploy, backend) {
|
|
6324
5972
|
if (database === "none") return "";
|
|
6325
5973
|
const isBackendSelf = backend === "self";
|
|
6326
5974
|
const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
|
|
@@ -6329,7 +5977,7 @@ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSet
|
|
|
6329
5977
|
if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6330
5978
|
|
|
6331
5979
|
1. Start the local SQLite database:
|
|
6332
|
-
${dbSetup === "d1" ?
|
|
5980
|
+
${dbSetup === "d1" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : `\`\`\`bash
|
|
6333
5981
|
cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
|
|
6334
5982
|
\`\`\`
|
|
6335
5983
|
`}
|
|
@@ -6427,11 +6075,6 @@ function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeplo
|
|
|
6427
6075
|
if (serverDeploy === "alchemy" && webDeploy !== "alchemy") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
|
|
6428
6076
|
if (webDeploy === "alchemy" && serverDeploy === "alchemy") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
|
|
6429
6077
|
}
|
|
6430
|
-
if (webDeploy === "wrangler" || serverDeploy === "wrangler") {
|
|
6431
|
-
lines.push("\n## Deployment (Cloudflare Wrangler)");
|
|
6432
|
-
if (webDeploy === "wrangler") lines.push(`- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`);
|
|
6433
|
-
if (serverDeploy === "wrangler") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`);
|
|
6434
|
-
}
|
|
6435
6078
|
return lines.length ? `\n${lines.join("\n")}\n` : "";
|
|
6436
6079
|
}
|
|
6437
6080
|
|
|
@@ -6555,7 +6198,6 @@ async function displayPostInstallInstructions(config) {
|
|
|
6555
6198
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6556
6199
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
6557
6200
|
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
6558
|
-
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6559
6201
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6560
6202
|
const hasWeb = frontend?.some((f) => [
|
|
6561
6203
|
"tanstack-router",
|
|
@@ -6575,7 +6217,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6575
6217
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
6576
6218
|
let stepCounter = 2;
|
|
6577
6219
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
6578
|
-
if (database === "sqlite" && dbSetup === "none" && (serverDeploy === "
|
|
6220
|
+
if (database === "sqlite" && dbSetup === "none" && (serverDeploy === "alchemy" || webDeploy === "alchemy")) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (starts local SQLite server for Workers compatibility)")}\n`;
|
|
6579
6221
|
if (isConvex) {
|
|
6580
6222
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
6581
6223
|
output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
|
|
@@ -6586,7 +6228,6 @@ async function displayPostInstallInstructions(config) {
|
|
|
6586
6228
|
if (runtime === "workers") {
|
|
6587
6229
|
if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
|
|
6588
6230
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6589
|
-
if (serverDeploy === "wrangler") output += `${pc.cyan(`${stepCounter++}.`)} cd apps/server && ${runCmd} cf-typegen\n`;
|
|
6590
6231
|
}
|
|
6591
6232
|
}
|
|
6592
6233
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
@@ -6604,7 +6245,6 @@ async function displayPostInstallInstructions(config) {
|
|
|
6604
6245
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
6605
6246
|
if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
|
|
6606
6247
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
6607
|
-
if (wranglerDeployInstructions) output += `\n${wranglerDeployInstructions.trim()}\n`;
|
|
6608
6248
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
6609
6249
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
6610
6250
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
@@ -6627,7 +6267,7 @@ function getNativeInstructions(isConvex, isBackendSelf, _frontend) {
|
|
|
6627
6267
|
function getLintingInstructions(runCmd) {
|
|
6628
6268
|
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
|
|
6629
6269
|
}
|
|
6630
|
-
async function getDatabaseInstructions(database, orm, runCmd,
|
|
6270
|
+
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, serverDeploy, _backend) {
|
|
6631
6271
|
const instructions = [];
|
|
6632
6272
|
if (dbSetup === "docker") {
|
|
6633
6273
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -6636,17 +6276,6 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
|
|
|
6636
6276
|
instructions.push("");
|
|
6637
6277
|
}
|
|
6638
6278
|
}
|
|
6639
|
-
if (serverDeploy === "wrangler" && dbSetup === "d1") {
|
|
6640
|
-
if (orm === "prisma" && runtime === "workers") instructions.push(`\n${pc.yellow("WARNING:")} Prisma + D1 on Workers with Wrangler has migration issues.\n Consider using Alchemy deploy instead of Wrangler for D1 projects.\n`);
|
|
6641
|
-
const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
|
|
6642
|
-
instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
|
|
6643
|
-
instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
|
|
6644
|
-
const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
|
|
6645
|
-
instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
|
|
6646
|
-
instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
|
|
6647
|
-
instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
|
|
6648
|
-
instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
|
|
6649
|
-
}
|
|
6650
6279
|
if (dbSetup === "d1" && serverDeploy === "alchemy") {
|
|
6651
6280
|
if (orm === "drizzle") instructions.push(`${pc.yellow("NOTE:")} D1 migrations are automatically handled by Alchemy`);
|
|
6652
6281
|
else if (orm === "prisma") {
|
|
@@ -6687,15 +6316,6 @@ function getNoOrmWarning() {
|
|
|
6687
6316
|
function getBunWebNativeWarning() {
|
|
6688
6317
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
6689
6318
|
}
|
|
6690
|
-
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6691
|
-
const instructions = [];
|
|
6692
|
-
if (webDeploy === "wrangler") {
|
|
6693
|
-
const deployPath = backend === "self" ? "apps/web" : "apps/web";
|
|
6694
|
-
instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} deploy`}`);
|
|
6695
|
-
}
|
|
6696
|
-
if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}`);
|
|
6697
|
-
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6698
|
-
}
|
|
6699
6319
|
function getClerkInstructions() {
|
|
6700
6320
|
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`;
|
|
6701
6321
|
}
|
|
@@ -6706,7 +6326,7 @@ function getPolarInstructions(backend) {
|
|
|
6706
6326
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
6707
6327
|
const instructions = [];
|
|
6708
6328
|
const isBackendSelf = backend === "self";
|
|
6709
|
-
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`}`);
|
|
6329
|
+
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
|
|
6710
6330
|
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`}`);
|
|
6711
6331
|
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`}`);
|
|
6712
6332
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|