create-better-t-stack 3.26.0 → 3.27.0
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 +8 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-BVuxTUEs.mjs → src-CiY2JajO.mjs} +138 -84
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -217,6 +217,12 @@ Create a Cloudflare Workers project:
|
|
|
217
217
|
npx create-better-t-stack --backend hono --runtime workers --database sqlite --orm drizzle --db-setup d1
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
+
Create a self-hosted fullstack project on Cloudflare with D1:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx create-better-t-stack --backend self --frontend next --api trpc --database sqlite --orm drizzle --db-setup d1 --web-deploy cloudflare
|
|
224
|
+
```
|
|
225
|
+
|
|
220
226
|
Create a minimal API-only project:
|
|
221
227
|
|
|
222
228
|
```bash
|
|
@@ -232,7 +238,8 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
|
|
|
232
238
|
- **Database 'none'**: Disables database setup and requires ORM to be `none`.
|
|
233
239
|
- **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
|
|
234
240
|
- **Runtime 'none'**: Only available with Convex backend, backend `none`, or backend `self`.
|
|
235
|
-
- **Cloudflare Workers runtime**: Only compatible with Hono backend
|
|
241
|
+
- **Cloudflare Workers runtime**: Only compatible with Hono backend. If a database is used, MongoDB is not supported.
|
|
242
|
+
- **Cloudflare D1 setup**: Requires `sqlite` and either `--runtime workers --server-deploy cloudflare` or `--backend self --web-deploy cloudflare`. For `backend self`, D1 is supported on `next`, `tanstack-start`, `nuxt`, and `astro`.
|
|
236
243
|
- **Addons 'none'**: Skips all addons.
|
|
237
244
|
- **Examples 'none'**: Skips all example implementations (todo, AI chat).
|
|
238
245
|
- **Nuxt, Svelte, SolidJS, and Astro** frontends are only compatible with oRPC API layer
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-
|
|
2
|
+
import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-CiY2JajO.mjs";
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-
|
|
2
|
+
import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-CiY2JajO.mjs";
|
|
3
3
|
export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
|
@@ -12,7 +12,7 @@ import envPaths from "env-paths";
|
|
|
12
12
|
import fs from "fs-extra";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { desktopWebFrontends as desktopWebFrontends$3 } from "@better-t-stack/types";
|
|
15
|
-
import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
|
|
15
|
+
import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
|
|
16
16
|
import { consola, createConsola } from "consola";
|
|
17
17
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
18
|
import gradient from "gradient-string";
|
|
@@ -728,6 +728,21 @@ const WEB_FRAMEWORKS = [
|
|
|
728
728
|
];
|
|
729
729
|
//#endregion
|
|
730
730
|
//#region src/utils/compatibility-rules.ts
|
|
731
|
+
const CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS = [
|
|
732
|
+
"nuxt",
|
|
733
|
+
"svelte",
|
|
734
|
+
"solid",
|
|
735
|
+
"astro"
|
|
736
|
+
];
|
|
737
|
+
const CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS = [
|
|
738
|
+
"tanstack-router",
|
|
739
|
+
"react-router",
|
|
740
|
+
"tanstack-start",
|
|
741
|
+
"next",
|
|
742
|
+
"native-bare",
|
|
743
|
+
"native-uniwind",
|
|
744
|
+
"native-unistyles"
|
|
745
|
+
];
|
|
731
746
|
function validationErr$1(message) {
|
|
732
747
|
return Result.err(new ValidationError({ message }));
|
|
733
748
|
}
|
|
@@ -781,7 +796,10 @@ function validateApiFrontendCompatibility(api, frontends = []) {
|
|
|
781
796
|
return Result.ok(void 0);
|
|
782
797
|
}
|
|
783
798
|
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
784
|
-
if (backend === "convex"
|
|
799
|
+
if (backend === "convex") {
|
|
800
|
+
if (auth === "better-auth" && CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS.includes(frontend)) return false;
|
|
801
|
+
if (frontend === "solid" || frontend === "astro") return false;
|
|
802
|
+
}
|
|
785
803
|
if (auth === "clerk") {
|
|
786
804
|
if ([
|
|
787
805
|
"nuxt",
|
|
@@ -792,6 +810,9 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
|
792
810
|
}
|
|
793
811
|
return true;
|
|
794
812
|
}
|
|
813
|
+
function supportsConvexBetterAuth(frontends = []) {
|
|
814
|
+
return frontends.some((frontend) => CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS.includes(frontend));
|
|
815
|
+
}
|
|
795
816
|
function allowedApisForFrontends(frontends = []) {
|
|
796
817
|
const includesNuxt = frontends.includes("nuxt");
|
|
797
818
|
const includesSvelte = frontends.includes("svelte");
|
|
@@ -3215,21 +3236,9 @@ async function getApiChoice(Api, frontend, backend) {
|
|
|
3215
3236
|
}
|
|
3216
3237
|
//#endregion
|
|
3217
3238
|
//#region src/prompts/auth.ts
|
|
3218
|
-
|
|
3219
|
-
if (
|
|
3220
|
-
|
|
3221
|
-
const supportedBetterAuthFrontends = frontend?.some((f) => [
|
|
3222
|
-
"tanstack-router",
|
|
3223
|
-
"tanstack-start",
|
|
3224
|
-
"next",
|
|
3225
|
-
"nuxt",
|
|
3226
|
-
"svelte",
|
|
3227
|
-
"solid",
|
|
3228
|
-
"native-bare",
|
|
3229
|
-
"native-uniwind",
|
|
3230
|
-
"native-unistyles"
|
|
3231
|
-
].includes(f));
|
|
3232
|
-
const hasClerkCompatibleFrontends = frontend?.some((f) => [
|
|
3239
|
+
function getAvailableAuthProviders(backend, frontend = []) {
|
|
3240
|
+
if (backend === "none") return ["none"];
|
|
3241
|
+
const hasClerkCompatibleFrontends = frontend.some((f) => [
|
|
3233
3242
|
"react-router",
|
|
3234
3243
|
"tanstack-router",
|
|
3235
3244
|
"tanstack-start",
|
|
@@ -3240,26 +3249,34 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
3240
3249
|
].includes(f));
|
|
3241
3250
|
const options = [];
|
|
3242
3251
|
if (backend === "convex") {
|
|
3243
|
-
if (
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3252
|
+
if (supportsConvexBetterAuth(frontend)) options.push("better-auth");
|
|
3253
|
+
} else options.push("better-auth");
|
|
3254
|
+
if (hasClerkCompatibleFrontends) options.push("clerk");
|
|
3255
|
+
if (options.length === 0) return ["none"];
|
|
3256
|
+
return [...options, "none"];
|
|
3257
|
+
}
|
|
3258
|
+
async function getAuthChoice(auth, backend, frontend = []) {
|
|
3259
|
+
if (auth !== void 0) return auth;
|
|
3260
|
+
const availableProviders = getAvailableAuthProviders(backend, frontend);
|
|
3261
|
+
if (availableProviders.length === 1 && availableProviders[0] === "none") return "none";
|
|
3262
|
+
const options = availableProviders.map((provider) => {
|
|
3263
|
+
switch (provider) {
|
|
3264
|
+
case "better-auth": return {
|
|
3265
|
+
value: "better-auth",
|
|
3266
|
+
label: "Better-Auth",
|
|
3267
|
+
hint: "comprehensive auth framework for TypeScript"
|
|
3268
|
+
};
|
|
3269
|
+
case "clerk": return {
|
|
3270
|
+
value: "clerk",
|
|
3271
|
+
label: "Clerk",
|
|
3272
|
+
hint: "More than auth, Complete User Management"
|
|
3273
|
+
};
|
|
3274
|
+
default: return {
|
|
3275
|
+
value: "none",
|
|
3276
|
+
label: "None",
|
|
3277
|
+
hint: "No auth"
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3263
3280
|
});
|
|
3264
3281
|
const response = await navigableSelect({
|
|
3265
3282
|
message: "Select authentication provider",
|
|
@@ -3375,7 +3392,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
|
3375
3392
|
label: "Turso",
|
|
3376
3393
|
hint: "SQLite for Production. Powered by libSQL"
|
|
3377
3394
|
},
|
|
3378
|
-
...runtime === "workers" ? [{
|
|
3395
|
+
...runtime === "workers" || backend === "self" ? [{
|
|
3379
3396
|
value: "d1",
|
|
3380
3397
|
label: "Cloudflare D1",
|
|
3381
3398
|
hint: "Cloudflare's managed, serverless database with SQLite's SQL semantics"
|
|
@@ -3815,9 +3832,10 @@ function getDeploymentDisplay(deployment) {
|
|
|
3815
3832
|
hint: `Add ${deployment} deployment`
|
|
3816
3833
|
};
|
|
3817
3834
|
}
|
|
3818
|
-
async function getDeploymentChoice(deployment, _runtime,
|
|
3835
|
+
async function getDeploymentChoice(deployment, _runtime, backend, frontend = [], dbSetup) {
|
|
3819
3836
|
if (deployment !== void 0) return deployment;
|
|
3820
3837
|
if (!hasWebFrontend(frontend)) return "none";
|
|
3838
|
+
if (backend === "self" && dbSetup === "d1") return "cloudflare";
|
|
3821
3839
|
const response = await navigableSelect({
|
|
3822
3840
|
message: "Select web deployment",
|
|
3823
3841
|
options: ["cloudflare", "none"].map((deploy) => {
|
|
@@ -3871,7 +3889,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
3871
3889
|
addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend, results.auth),
|
|
3872
3890
|
examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
|
|
3873
3891
|
dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
|
|
3874
|
-
webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
|
|
3892
|
+
webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend, results.dbSetup),
|
|
3875
3893
|
serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
|
|
3876
3894
|
git: () => getGitChoice(flags.git),
|
|
3877
3895
|
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
|
@@ -4288,6 +4306,18 @@ function validateArrayOptions(options) {
|
|
|
4288
4306
|
function validationErr(message) {
|
|
4289
4307
|
return Result.err(new ValidationError({ message }));
|
|
4290
4308
|
}
|
|
4309
|
+
function hasResolvedWorkersD1Target(config) {
|
|
4310
|
+
return config.backend === "hono" && config.runtime === "workers" && config.serverDeploy === "cloudflare";
|
|
4311
|
+
}
|
|
4312
|
+
function hasResolvedSelfCloudflareD1Target(config) {
|
|
4313
|
+
return config.backend === "self" && config.runtime === "none" && config.webDeploy === "cloudflare";
|
|
4314
|
+
}
|
|
4315
|
+
function canResolveWorkersD1Target(config) {
|
|
4316
|
+
return (config.backend === void 0 || config.backend === "hono") && (config.runtime === void 0 || config.runtime === "workers") && (config.serverDeploy === void 0 || config.serverDeploy === "cloudflare");
|
|
4317
|
+
}
|
|
4318
|
+
function canResolveSelfCloudflareD1Target(config) {
|
|
4319
|
+
return (config.backend === void 0 || config.backend === "self") && (config.runtime === void 0 || config.runtime === "none") && (config.webDeploy === void 0 || config.webDeploy === "cloudflare");
|
|
4320
|
+
}
|
|
4291
4321
|
function validateDatabaseOrmAuth(cfg, flags) {
|
|
4292
4322
|
const db = cfg.database;
|
|
4293
4323
|
const orm = cfg.orm;
|
|
@@ -4326,8 +4356,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
4326
4356
|
},
|
|
4327
4357
|
d1: {
|
|
4328
4358
|
database: "sqlite",
|
|
4329
|
-
|
|
4330
|
-
errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
|
|
4359
|
+
errorMessage: "Cloudflare D1 setup requires SQLite database."
|
|
4331
4360
|
},
|
|
4332
4361
|
docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
|
|
4333
4362
|
none: { errorMessage: "" }
|
|
@@ -4338,6 +4367,13 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
4338
4367
|
if (database !== "postgres" && database !== "mysql") return validationErr(validation.errorMessage);
|
|
4339
4368
|
} else if (validation.database && database !== validation.database) return validationErr(validation.errorMessage);
|
|
4340
4369
|
if (validation.runtime && runtime !== validation.runtime) return validationErr(validation.errorMessage);
|
|
4370
|
+
if (dbSetup === "d1") {
|
|
4371
|
+
const isWorkersTarget = hasResolvedWorkersD1Target(config);
|
|
4372
|
+
const isSelfCloudflareTarget = hasResolvedSelfCloudflareD1Target(config);
|
|
4373
|
+
const canResolveWorkersTarget = canResolveWorkersD1Target(config);
|
|
4374
|
+
const canResolveSelfCloudflareTarget = canResolveSelfCloudflareD1Target(config);
|
|
4375
|
+
if (!isWorkersTarget && !isSelfCloudflareTarget && !canResolveWorkersTarget && !canResolveSelfCloudflareTarget) return validationErr("Cloudflare D1 setup requires SQLite database and either Cloudflare Workers runtime with server deployment or backend 'self' with Cloudflare web deployment.");
|
|
4376
|
+
}
|
|
4341
4377
|
if (dbSetup === "docker") {
|
|
4342
4378
|
if (database === "sqlite") return validationErr("Docker setup is not compatible with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
|
|
4343
4379
|
if (runtime === "workers") return validationErr("Docker setup is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
@@ -4356,15 +4392,10 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
4356
4392
|
if (has("dbSetup") && config.dbSetup !== "none") return validationErr("Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
|
|
4357
4393
|
if (has("serverDeploy") && config.serverDeploy !== "none") return validationErr("Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
|
|
4358
4394
|
if (has("auth") && config.auth === "better-auth") {
|
|
4359
|
-
const
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
"native-bare",
|
|
4364
|
-
"native-uniwind",
|
|
4365
|
-
"native-unistyles"
|
|
4366
|
-
];
|
|
4367
|
-
if (!config.frontend?.some((f) => supportedFrontends.includes(f))) return validationErr("Better-Auth with Convex backend requires a supported frontend (TanStack Router, TanStack Start, Next.js, or Native).");
|
|
4395
|
+
const incompatibleFrontends = config.frontend?.filter((f) => CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS.includes(f)) ?? [];
|
|
4396
|
+
const hasSupportedFrontend = supportsConvexBetterAuth(config.frontend);
|
|
4397
|
+
if (incompatibleFrontends.length > 0) return validationErr(`Better Auth with '--backend convex' is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please use a React-based web frontend (next, tanstack-start, tanstack-router, react-router), a supported native frontend, or choose a different auth provider.`);
|
|
4398
|
+
if (!hasSupportedFrontend) return validationErr(`Better Auth with '--backend convex' requires a supported frontend (${CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS.join(", ")}).`);
|
|
4368
4399
|
}
|
|
4369
4400
|
return Result.ok(void 0);
|
|
4370
4401
|
}
|
|
@@ -4600,8 +4631,8 @@ async function addEnvVariablesToFile(envPath, variables) {
|
|
|
4600
4631
|
//#endregion
|
|
4601
4632
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4602
4633
|
async function setupCloudflareD1(config) {
|
|
4603
|
-
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4604
|
-
if (!(serverDeploy === "cloudflare" &&
|
|
4634
|
+
const { projectDir, serverDeploy, webDeploy, orm, backend } = config;
|
|
4635
|
+
if (!(orm === "prisma" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare"))) return Result.ok(void 0);
|
|
4605
4636
|
return Result.tryPromise({
|
|
4606
4637
|
try: async () => {
|
|
4607
4638
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
@@ -5983,7 +6014,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
5983
6014
|
const hasHusky = addons?.includes("husky");
|
|
5984
6015
|
const hasLefthook = addons?.includes("lefthook");
|
|
5985
6016
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
|
|
5986
|
-
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6017
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, webDeploy, serverDeploy, backend) : "";
|
|
5987
6018
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
|
|
5988
6019
|
const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
|
|
5989
6020
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
@@ -5998,9 +6029,10 @@ async function displayPostInstallInstructions(config) {
|
|
|
5998
6029
|
const hasWeb = frontend?.some((f) => types_exports.desktopWebFrontends.includes(f));
|
|
5999
6030
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
6000
6031
|
const hasReactRouter = frontend?.includes("react-router");
|
|
6032
|
+
const hasTanStackRouter = frontend?.includes("tanstack-router");
|
|
6001
6033
|
const hasSvelte = frontend?.includes("svelte");
|
|
6002
6034
|
const hasAstro = frontend?.includes("astro");
|
|
6003
|
-
const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
|
|
6035
|
+
const webPort = hasReactRouter || hasTanStackRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
|
|
6004
6036
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
6005
6037
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
6006
6038
|
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
@@ -6075,8 +6107,9 @@ function getLefthookInstructions(packageManager) {
|
|
|
6075
6107
|
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
6076
6108
|
return `${pc.bold("Git hooks with Lefthook:")}\n${pc.cyan("•")} Install hooks: ${cmd} lefthook install\n`;
|
|
6077
6109
|
}
|
|
6078
|
-
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, serverDeploy,
|
|
6110
|
+
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend) {
|
|
6079
6111
|
const instructions = [];
|
|
6112
|
+
const isD1Alchemy = dbSetup === "d1" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare");
|
|
6080
6113
|
if (dbSetup === "docker") {
|
|
6081
6114
|
const dockerStatus = await getDockerStatus(database);
|
|
6082
6115
|
if (dockerStatus.message) {
|
|
@@ -6084,7 +6117,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
6084
6117
|
instructions.push("");
|
|
6085
6118
|
}
|
|
6086
6119
|
}
|
|
6087
|
-
if (
|
|
6120
|
+
if (isD1Alchemy) {
|
|
6088
6121
|
if (orm === "drizzle") instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
|
|
6089
6122
|
else if (orm === "prisma") {
|
|
6090
6123
|
instructions.push(`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`);
|
|
@@ -6099,15 +6132,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
6099
6132
|
if (orm === "prisma") {
|
|
6100
6133
|
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
|
|
6101
6134
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6102
|
-
if (!
|
|
6135
|
+
if (!isD1Alchemy) {
|
|
6103
6136
|
instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
|
|
6104
6137
|
instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6105
6138
|
}
|
|
6106
|
-
if (!
|
|
6139
|
+
if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6107
6140
|
} else if (orm === "drizzle") {
|
|
6108
6141
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6109
|
-
if (
|
|
6110
|
-
if (!
|
|
6142
|
+
if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
6143
|
+
if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
6111
6144
|
} else if (orm === "mongoose") {
|
|
6112
6145
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
6113
6146
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
@@ -6819,30 +6852,51 @@ async function builder() {
|
|
|
6819
6852
|
* ```
|
|
6820
6853
|
*/
|
|
6821
6854
|
async function createVirtual(options) {
|
|
6855
|
+
const config = {
|
|
6856
|
+
projectName: options.projectName || "my-project",
|
|
6857
|
+
projectDir: "/virtual",
|
|
6858
|
+
relativePath: "./virtual",
|
|
6859
|
+
addonOptions: options.addonOptions,
|
|
6860
|
+
dbSetupOptions: options.dbSetupOptions,
|
|
6861
|
+
database: options.database || "none",
|
|
6862
|
+
orm: options.orm || "none",
|
|
6863
|
+
backend: options.backend || "hono",
|
|
6864
|
+
runtime: options.runtime || "bun",
|
|
6865
|
+
frontend: options.frontend || ["tanstack-router"],
|
|
6866
|
+
addons: options.addons || [],
|
|
6867
|
+
examples: options.examples || [],
|
|
6868
|
+
auth: options.auth || "none",
|
|
6869
|
+
payments: options.payments || "none",
|
|
6870
|
+
git: options.git ?? false,
|
|
6871
|
+
packageManager: options.packageManager || "bun",
|
|
6872
|
+
install: false,
|
|
6873
|
+
dbSetup: options.dbSetup || "none",
|
|
6874
|
+
api: options.api || "trpc",
|
|
6875
|
+
webDeploy: options.webDeploy || "none",
|
|
6876
|
+
serverDeploy: options.serverDeploy || "none"
|
|
6877
|
+
};
|
|
6878
|
+
const validationResult = validateConfigCompatibility(config, new Set([
|
|
6879
|
+
"database",
|
|
6880
|
+
"orm",
|
|
6881
|
+
"backend",
|
|
6882
|
+
"runtime",
|
|
6883
|
+
"frontend",
|
|
6884
|
+
"addons",
|
|
6885
|
+
"examples",
|
|
6886
|
+
"auth",
|
|
6887
|
+
"dbSetup",
|
|
6888
|
+
"payments",
|
|
6889
|
+
"api",
|
|
6890
|
+
"webDeploy",
|
|
6891
|
+
"serverDeploy"
|
|
6892
|
+
]), config);
|
|
6893
|
+
if (validationResult.isErr()) return Result.err(new GeneratorError({
|
|
6894
|
+
message: validationResult.error.message,
|
|
6895
|
+
phase: "validation",
|
|
6896
|
+
cause: validationResult.error
|
|
6897
|
+
}));
|
|
6822
6898
|
return generate({
|
|
6823
|
-
config
|
|
6824
|
-
projectName: options.projectName || "my-project",
|
|
6825
|
-
projectDir: "/virtual",
|
|
6826
|
-
relativePath: "./virtual",
|
|
6827
|
-
addonOptions: options.addonOptions,
|
|
6828
|
-
dbSetupOptions: options.dbSetupOptions,
|
|
6829
|
-
database: options.database || "none",
|
|
6830
|
-
orm: options.orm || "none",
|
|
6831
|
-
backend: options.backend || "hono",
|
|
6832
|
-
runtime: options.runtime || "bun",
|
|
6833
|
-
frontend: options.frontend || ["tanstack-router"],
|
|
6834
|
-
addons: options.addons || [],
|
|
6835
|
-
examples: options.examples || [],
|
|
6836
|
-
auth: options.auth || "none",
|
|
6837
|
-
payments: options.payments || "none",
|
|
6838
|
-
git: options.git ?? false,
|
|
6839
|
-
packageManager: options.packageManager || "bun",
|
|
6840
|
-
install: false,
|
|
6841
|
-
dbSetup: options.dbSetup || "none",
|
|
6842
|
-
api: options.api || "trpc",
|
|
6843
|
-
webDeploy: options.webDeploy || "none",
|
|
6844
|
-
serverDeploy: options.serverDeploy || "none"
|
|
6845
|
-
},
|
|
6899
|
+
config,
|
|
6846
6900
|
templates: EMBEDDED_TEMPLATES
|
|
6847
6901
|
});
|
|
6848
6902
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.27.0",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
"prepublishOnly": "npm run build"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@better-t-stack/template-generator": "^3.
|
|
74
|
-
"@better-t-stack/types": "^3.
|
|
73
|
+
"@better-t-stack/template-generator": "^3.27.0",
|
|
74
|
+
"@better-t-stack/types": "^3.27.0",
|
|
75
75
|
"@clack/core": "^1.1.0",
|
|
76
76
|
"@clack/prompts": "^1.1.0",
|
|
77
77
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
78
|
-
"@trpc/server": "^11.4
|
|
78
|
+
"@trpc/server": "^11.13.4",
|
|
79
79
|
"better-result": "^2.7.0",
|
|
80
80
|
"consola": "^3.4.2",
|
|
81
81
|
"env-paths": "^4.0.0",
|