From 51cfb359123b1269be08147be96661d73e47d844 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Fri, 8 Aug 2025 16:00:10 +0530 Subject: [PATCH] feat(web): improve docs and refactor cli (#476) --- README.md | 36 +- apps/cli/src/constants.ts | 11 - apps/cli/src/prompts/api.ts | 61 +-- apps/cli/src/prompts/examples.ts | 22 +- apps/cli/src/prompts/frontend.ts | 10 +- apps/cli/src/prompts/project-name.ts | 15 +- apps/cli/src/prompts/web-deploy.ts | 3 +- apps/cli/src/types.ts | 6 +- apps/cli/src/utils/compatibility-rules.ts | 315 ++++++++++++ apps/cli/src/utils/compatibility.ts | 11 + .../utils/generate-reproducible-command.ts | 10 +- apps/cli/src/validation.ts | 483 ++++++------------ apps/web/content/docs/analytics.mdx | 55 ++ apps/web/content/docs/bts-config.mdx | 54 ++ apps/web/content/docs/cli-commands.mdx | 213 ++++++-- apps/web/content/docs/cli/add.mdx | 35 ++ apps/web/content/docs/cli/builder.mdx | 25 + apps/web/content/docs/cli/docs.mdx | 25 + apps/web/content/docs/cli/index.mdx | 23 + apps/web/content/docs/cli/init.mdx | 35 ++ apps/web/content/docs/cli/meta.json | 5 + apps/web/content/docs/cli/sponsors.mdx | 25 + apps/web/content/docs/compatibility.mdx | 29 ++ apps/web/content/docs/contributing.mdx | 94 ++++ apps/web/content/docs/faq.mdx | 342 +------------ apps/web/content/docs/index.mdx | 199 ++++---- apps/web/content/docs/installation.mdx | 236 --------- apps/web/content/docs/meta.json | 32 +- apps/web/content/docs/project-structure.mdx | 60 +++ .../web/src/app/(home)/_components/footer.tsx | 4 +- .../web/src/app/(home)/_components/navbar.tsx | 2 +- .../_components/analytics-header.tsx | 2 +- apps/web/src/app/docs/layout.tsx | 10 +- bun.lock | 2 +- 34 files changed, 1336 insertions(+), 1154 deletions(-) create mode 100644 apps/cli/src/utils/compatibility-rules.ts create mode 100644 apps/cli/src/utils/compatibility.ts create mode 100644 apps/web/content/docs/analytics.mdx create mode 100644 apps/web/content/docs/bts-config.mdx create mode 100644 apps/web/content/docs/cli/add.mdx create mode 100644 apps/web/content/docs/cli/builder.mdx create mode 100644 apps/web/content/docs/cli/docs.mdx create mode 100644 apps/web/content/docs/cli/index.mdx create mode 100644 apps/web/content/docs/cli/init.mdx create mode 100644 apps/web/content/docs/cli/meta.json create mode 100644 apps/web/content/docs/cli/sponsors.mdx create mode 100644 apps/web/content/docs/compatibility.mdx create mode 100644 apps/web/content/docs/contributing.mdx delete mode 100644 apps/web/content/docs/installation.mdx create mode 100644 apps/web/content/docs/project-structure.mdx diff --git a/README.md b/README.md index 68c6c25..c2cb225 100644 --- a/README.md +++ b/README.md @@ -25,33 +25,36 @@ pnpm create better-t-stack@latest ## Features -- **Zero-config setup** with interactive CLI wizard -- **End-to-end type safety** from database to frontend via tRPC -- **Modern stack** with React, Hono/Elysia, and TanStack libraries -- **Multi-platform** supporting web, mobile (Expo), and desktop applications -- **Database flexibility** with SQLite (Turso) or PostgreSQL options -- **ORM choice** between Drizzle or Prisma -- **Built-in authentication** with Better-Auth -- **Optional PWA support** for installable web applications -- **Desktop app capabilities** with Tauri integration -- **Monorepo architecture** powered by Turborepo +- Frontend: React (TanStack Router, React Router, TanStack Start), Next.js, Nuxt, Svelte, Solid, React Native (NativeWind/Unistyles), or none +- Backend: Hono, Express, Fastify, Elysia, Next API Routes, Convex, or none +- API: tRPC or oRPC (or none) +- Runtime: Bun, Node.js, or Cloudflare Workers +- Databases: SQLite, PostgreSQL, MySQL, MongoDB (or none) +- ORMs: Drizzle, Prisma, Mongoose (or none) +- Auth: Better-Auth (optional) +- Addons: Turborepo, PWA, Tauri, Biome, Husky, Starlight, Fumadocs, Ultracite, Oxlint +- Examples: Todo, AI +- DB Setup: Turso, Neon, Supabase, Prisma PostgreSQL, MongoDB Atlas, Cloudflare D1, Docker +- Web Deploy: Cloudflare Workers + +Type safety end-to-end, clean monorepo layout, and zero‑lock‑in: you choose only what you need. ## Repository Structure This repository is organized as a monorepo containing: -- **CLI**: [`create-better-t-stack`](apps/cli) - The scaffolding CLI tool -- **Documentation**: [`web`](apps/web) - Official website and documentation +- **CLI**: [`apps/cli`](apps/cli) - The scaffolding CLI tool +- **Documentation**: [`apps/web`](apps/web) - Official website and documentation ## Documentation -Visit [better-t-stack.dev](https://better-t-stack.dev) for full documentation, guides, and examples. +Visit [better-t-stack.dev](https://better-t-stack.dev) for full documentation, guides, and examples. You can also use the visual Stack Builder at `https://better-t-stack.dev/new` to generate a command for your stack. ## Development ```bash # Clone the repository -git clone https://github.com/better-t-stack/create-better-t-stack.git +git clone https://github.com/AmanVarshney01/create-better-t-stack.git # Install dependencies bun install @@ -65,7 +68,10 @@ bun dev:web ## Want to contribute? -Just fork the repository and submit a pull request! +Please read the Contribution Guide first and open an issue before starting new features to ensure alignment with project goals. + +- Docs: [`Contributing`](/apps/web/content/docs/contributing.mdx) +- Repo guide: [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) ## Star History diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index cdd5511..2130559 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -139,14 +139,3 @@ export const ADDON_COMPATIBILITY: Record = { fumadocs: [], none: [], } as const; - -// TODO: need to refactor this -export const WEB_FRAMEWORKS: readonly Frontend[] = [ - "tanstack-router", - "react-router", - "tanstack-start", - "next", - "nuxt", - "svelte", - "solid", -]; diff --git a/apps/cli/src/prompts/api.ts b/apps/cli/src/prompts/api.ts index 48d12b3..26b0c78 100644 --- a/apps/cli/src/prompts/api.ts +++ b/apps/cli/src/prompts/api.ts @@ -1,6 +1,7 @@ import { cancel, isCancel, select } from "@clack/prompts"; import pc from "picocolors"; import type { API, Backend, Frontend } from "../types"; +import { allowedApisForFrontends } from "../utils/compatibility-rules"; export async function getApiChoice( Api?: API | undefined, @@ -11,46 +12,30 @@ export async function getApiChoice( return "none"; } - if (Api) return Api; + const allowed = allowedApisForFrontends(frontend ?? []); - const includesNuxt = frontend?.includes("nuxt"); - const includesSvelte = frontend?.includes("svelte"); - const includesSolid = frontend?.includes("solid"); - - let apiOptions = [ - { - value: "trpc" as const, - label: "tRPC", - hint: "End-to-end typesafe APIs made easy", - }, - { - value: "orpc" as const, - label: "oRPC", - hint: "End-to-end type-safe APIs that adhere to OpenAPI standards", - }, - { - value: "none" as const, - label: "None", - hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)", - }, - ]; - - if (includesNuxt || includesSvelte || includesSolid) { - apiOptions = [ - { - value: "orpc" as const, - label: "oRPC", - hint: `End-to-end type-safe APIs (Recommended for ${ - includesNuxt ? "Nuxt" : includesSvelte ? "Svelte" : "Solid" - } frontend)`, - }, - { - value: "none" as const, - label: "None", - hint: "No API layer", - }, - ]; + if (Api) { + return allowed.includes(Api) ? Api : allowed[0]; } + const apiOptions = allowed.map((a) => + a === "trpc" + ? { + value: "trpc" as const, + label: "tRPC", + hint: "End-to-end typesafe APIs made easy", + } + : a === "orpc" + ? { + value: "orpc" as const, + label: "oRPC", + hint: "End-to-end type-safe APIs that adhere to OpenAPI standards", + } + : { + value: "none" as const, + label: "None", + hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)", + }, + ); const apiType = await select({ message: "Select API type", diff --git a/apps/cli/src/prompts/examples.ts b/apps/cli/src/prompts/examples.ts index 3d39ada..13244ed 100644 --- a/apps/cli/src/prompts/examples.ts +++ b/apps/cli/src/prompts/examples.ts @@ -2,6 +2,10 @@ import { cancel, isCancel, multiselect } from "@clack/prompts"; import pc from "picocolors"; import { DEFAULT_CONFIG } from "../constants"; import type { API, Backend, Database, Examples, Frontend } from "../types"; +import { + isExampleAIAllowed, + isExampleTodoAllowed, +} from "../utils/compatibility-rules"; export async function getExamplesChoice( examples?: Examples[], @@ -30,15 +34,17 @@ export async function getExamplesChoice( if (noFrontendSelected) return []; let response: Examples[] | symbol = []; - const options: { value: Examples; label: string; hint: string }[] = [ - { + const options: { value: Examples; label: string; hint: string }[] = []; + + if (isExampleTodoAllowed(backend, database)) { + options.push({ value: "todo" as const, label: "Todo App", hint: "A simple CRUD example app", - }, - ]; + }); + } - if (backend !== "elysia" && !frontends?.includes("solid")) { + if (isExampleAIAllowed(backend, frontends ?? [])) { options.push({ value: "ai" as const, label: "AI Chat", @@ -46,11 +52,15 @@ export async function getExamplesChoice( }); } + if (options.length === 0) return []; + response = await multiselect({ message: "Include examples", options: options, required: false, - initialValues: DEFAULT_CONFIG.examples, + initialValues: DEFAULT_CONFIG.examples?.filter((ex) => + options.some((o) => o.value === ex), + ), }); if (isCancel(response)) { diff --git a/apps/cli/src/prompts/frontend.ts b/apps/cli/src/prompts/frontend.ts index e4eb9c0..c337ada 100644 --- a/apps/cli/src/prompts/frontend.ts +++ b/apps/cli/src/prompts/frontend.ts @@ -2,6 +2,7 @@ import { cancel, isCancel, multiselect, select } from "@clack/prompts"; import pc from "picocolors"; import { DEFAULT_CONFIG } from "../constants"; import type { Backend, Frontend } from "../types"; +import { isFrontendAllowedWithBackend } from "../utils/compatibility-rules"; export async function getFrontendChoice( frontendOptions?: Frontend[], @@ -73,12 +74,9 @@ export async function getFrontendChoice( }, ]; - const webOptions = allWebOptions.filter((option) => { - if (backend === "convex") { - return option.value !== "solid"; - } - return true; - }); + const webOptions = allWebOptions.filter((option) => + isFrontendAllowedWithBackend(option.value, backend), + ); const webFramework = await select({ message: "Choose web", diff --git a/apps/cli/src/prompts/project-name.ts b/apps/cli/src/prompts/project-name.ts index 0b5be85..762a163 100644 --- a/apps/cli/src/prompts/project-name.ts +++ b/apps/cli/src/prompts/project-name.ts @@ -1,10 +1,17 @@ import path from "node:path"; import { cancel, isCancel, text } from "@clack/prompts"; +import consola from "consola"; import fs from "fs-extra"; import pc from "picocolors"; import { DEFAULT_CONFIG } from "../constants"; import { ProjectNameSchema } from "../types"; +function isPathWithinCwd(targetPath: string): boolean { + const resolved = path.resolve(targetPath); + const rel = path.relative(process.cwd(), resolved); + return !rel.startsWith("..") && !path.isAbsolute(rel); +} + function validateDirectoryName(name: string): string | undefined { if (name === ".") return undefined; @@ -23,7 +30,11 @@ export async function getProjectName(initialName?: string): Promise { const finalDirName = path.basename(initialName); const validationError = validateDirectoryName(finalDirName); if (!validationError) { - return initialName; + const projectDir = path.resolve(process.cwd(), initialName); + if (isPathWithinCwd(projectDir)) { + return initialName; + } + consola.error(pc.red("Project path must be within current directory")); } } @@ -56,7 +67,7 @@ export async function getProjectName(initialName?: string): Promise { if (nameToUse !== ".") { const projectDir = path.resolve(process.cwd(), nameToUse); - if (!projectDir.startsWith(process.cwd())) { + if (!isPathWithinCwd(projectDir)) { return "Project path must be within current directory"; } } diff --git a/apps/cli/src/prompts/web-deploy.ts b/apps/cli/src/prompts/web-deploy.ts index 8622556..e948758 100644 --- a/apps/cli/src/prompts/web-deploy.ts +++ b/apps/cli/src/prompts/web-deploy.ts @@ -1,7 +1,8 @@ import { cancel, isCancel, select } from "@clack/prompts"; import pc from "picocolors"; -import { DEFAULT_CONFIG, WEB_FRAMEWORKS } from "../constants"; +import { DEFAULT_CONFIG } from "../constants"; import type { Backend, Frontend, Runtime, WebDeploy } from "../types"; +import { WEB_FRAMEWORKS } from "../utils/compatibility"; function hasWebFrontend(frontends: Frontend[]): boolean { return frontends.some((f) => WEB_FRAMEWORKS.includes(f)); diff --git a/apps/cli/src/types.ts b/apps/cli/src/types.ts index 536a174..4997ceb 100644 --- a/apps/cli/src/types.ts +++ b/apps/cli/src/types.ts @@ -17,9 +17,7 @@ export type Backend = z.infer; export const RuntimeSchema = z .enum(["bun", "node", "workers", "none"]) - .describe( - "Runtime environment (workers only available with hono backend and drizzle orm)", - ); + .describe("Runtime environment"); export type Runtime = z.infer; export const FrontendSchema = z @@ -176,5 +174,3 @@ export interface BetterTStackConfig { api: API; webDeploy: WebDeploy; } - -export type AvailablePackageManagers = "npm" | "pnpm" | "bun"; diff --git a/apps/cli/src/utils/compatibility-rules.ts b/apps/cli/src/utils/compatibility-rules.ts new file mode 100644 index 0000000..637abdc --- /dev/null +++ b/apps/cli/src/utils/compatibility-rules.ts @@ -0,0 +1,315 @@ +import { consola } from "consola"; +import type { + Addons, + API, + CLIInput, + Frontend, + ProjectConfig, + WebDeploy, +} from "../types"; +import { validateAddonCompatibility } from "./addon-compatibility"; +import { WEB_FRAMEWORKS } from "./compatibility"; + +export function isWebFrontend(value: Frontend): boolean { + return WEB_FRAMEWORKS.includes(value); +} + +export function splitFrontends(values: Frontend[] = []): { + web: Frontend[]; + native: Frontend[]; +} { + const web = values.filter((f) => isWebFrontend(f)); + const native = values.filter( + (f) => f === "native-nativewind" || f === "native-unistyles", + ); + return { web, native }; +} + +export function ensureSingleWebAndNative(frontends: Frontend[]) { + const { web, native } = splitFrontends(frontends); + if (web.length > 1) { + consola.fatal( + "Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid", + ); + process.exit(1); + } + if (native.length > 1) { + consola.fatal( + "Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles", + ); + process.exit(1); + } +} + +export function validateWorkersCompatibility( + providedFlags: Set, + options: CLIInput, + config: Partial, +) { + if ( + providedFlags.has("runtime") && + options.runtime === "workers" && + config.backend && + config.backend !== "hono" + ) { + consola.fatal( + `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.`, + ); + process.exit(1); + } + + if ( + providedFlags.has("backend") && + config.backend && + config.backend !== "hono" && + config.runtime === "workers" + ) { + consola.fatal( + `Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`, + ); + process.exit(1); + } + + if ( + providedFlags.has("runtime") && + options.runtime === "workers" && + config.orm && + config.orm !== "drizzle" && + config.orm !== "none" + ) { + consola.fatal( + `Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`, + ); + process.exit(1); + } + + if ( + providedFlags.has("orm") && + config.orm && + config.orm !== "drizzle" && + config.orm !== "none" && + config.runtime === "workers" + ) { + consola.fatal( + `ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`, + ); + process.exit(1); + } + + if ( + providedFlags.has("runtime") && + options.runtime === "workers" && + config.database === "mongodb" + ) { + consola.fatal( + "Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.", + ); + process.exit(1); + } + + if ( + providedFlags.has("runtime") && + options.runtime === "workers" && + config.dbSetup === "docker" + ) { + consola.fatal( + "Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. 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.", + ); + process.exit(1); + } + + if ( + providedFlags.has("database") && + config.database === "mongodb" && + config.runtime === "workers" + ) { + consola.fatal( + "MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.", + ); + process.exit(1); + } + + if ( + providedFlags.has("dbSetup") && + options.dbSetup === "docker" && + config.runtime === "workers" + ) { + consola.fatal( + "Docker setup (--db-setup docker) 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.", + ); + process.exit(1); + } +} + +export function coerceBackendPresets(config: Partial) { + if (config.backend === "convex") { + config.auth = false; + config.database = "none"; + config.orm = "none"; + config.api = "none"; + config.runtime = "none"; + config.dbSetup = "none"; + config.examples = ["todo"] as ProjectConfig["examples"]; + } + if (config.backend === "none") { + config.auth = false; + config.database = "none"; + config.orm = "none"; + config.api = "none"; + config.runtime = "none"; + config.dbSetup = "none"; + config.examples = [] as ProjectConfig["examples"]; + } +} + +export function incompatibleFlagsForBackend( + backend: ProjectConfig["backend"] | undefined, + providedFlags: Set, + options: CLIInput, +): string[] { + const list: string[] = []; + if (backend === "convex") { + if (providedFlags.has("auth") && options.auth === true) list.push("--auth"); + if (providedFlags.has("database") && options.database !== "none") + list.push(`--database ${options.database}`); + if (providedFlags.has("orm") && options.orm !== "none") + list.push(`--orm ${options.orm}`); + if (providedFlags.has("api") && options.api !== "none") + list.push(`--api ${options.api}`); + if (providedFlags.has("runtime") && options.runtime !== "none") + list.push(`--runtime ${options.runtime}`); + if (providedFlags.has("dbSetup") && options.dbSetup !== "none") + list.push(`--db-setup ${options.dbSetup}`); + } + if (backend === "none") { + if (providedFlags.has("auth") && options.auth === true) list.push("--auth"); + if (providedFlags.has("database") && options.database !== "none") + list.push(`--database ${options.database}`); + if (providedFlags.has("orm") && options.orm !== "none") + list.push(`--orm ${options.orm}`); + if (providedFlags.has("api") && options.api !== "none") + list.push(`--api ${options.api}`); + if (providedFlags.has("runtime") && options.runtime !== "none") + list.push(`--runtime ${options.runtime}`); + if (providedFlags.has("dbSetup") && options.dbSetup !== "none") + list.push(`--db-setup ${options.dbSetup}`); + if (providedFlags.has("examples") && options.examples) { + const hasNonNoneExamples = options.examples.some((ex) => ex !== "none"); + if (hasNonNoneExamples) list.push("--examples"); + } + } + return list; +} + +export function validateApiFrontendCompatibility( + api: API | undefined, + frontends: Frontend[] = [], +) { + const includesNuxt = frontends.includes("nuxt"); + const includesSvelte = frontends.includes("svelte"); + const includesSolid = frontends.includes("solid"); + if ((includesNuxt || includesSvelte || includesSolid) && api === "trpc") { + consola.fatal( + `tRPC API is not supported with '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' frontend. Please use --api orpc or --api none or remove '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' from --frontend.`, + ); + process.exit(1); + } +} + +export function isFrontendAllowedWithBackend( + frontend: Frontend, + backend?: ProjectConfig["backend"], +) { + if (backend === "convex" && frontend === "solid") return false; + return true; +} + +export function allowedApisForFrontends(frontends: Frontend[] = []): API[] { + const includesNuxt = frontends.includes("nuxt"); + const includesSvelte = frontends.includes("svelte"); + const includesSolid = frontends.includes("solid"); + const base: API[] = ["trpc", "orpc", "none"]; + if (includesNuxt || includesSvelte || includesSolid) { + return ["orpc", "none"]; + } + return base; +} + +export function isExampleTodoAllowed( + backend?: ProjectConfig["backend"], + database?: ProjectConfig["database"], +) { + return !(backend !== "convex" && backend !== "none" && database === "none"); +} + +export function isExampleAIAllowed( + backend?: ProjectConfig["backend"], + frontends: Frontend[] = [], +) { + const includesSolid = frontends.includes("solid"); + if (backend === "elysia") return false; + if (includesSolid) return false; + return true; +} + +export function validateWebDeployRequiresWebFrontend( + webDeploy: WebDeploy | undefined, + hasWebFrontendFlag: boolean, +) { + if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) { + consola.fatal( + "'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.", + ); + process.exit(1); + } +} + +export function validateAddonsAgainstFrontends( + addons: Addons[] = [], + frontends: Frontend[] = [], +) { + for (const addon of addons) { + if (addon === "none") continue; + const { isCompatible, reason } = validateAddonCompatibility( + addon, + frontends, + ); + if (!isCompatible) { + consola.fatal(`Incompatible addon/frontend combination: ${reason}`); + process.exit(1); + } + } +} + +export function validateExamplesCompatibility( + examples: string[] | undefined, + backend: ProjectConfig["backend"] | undefined, + database: ProjectConfig["database"] | undefined, + frontend?: Frontend[], +) { + const examplesArr = examples ?? []; + if (examplesArr.length === 0 || examplesArr.includes("none")) return; + if ( + examplesArr.includes("todo") && + backend !== "convex" && + backend !== "none" && + database === "none" + ) { + consola.fatal( + "The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.", + ); + process.exit(1); + } + if (examplesArr.includes("ai") && backend === "elysia") { + consola.fatal( + "The 'ai' example is not compatible with the Elysia backend.", + ); + process.exit(1); + } + if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) { + consola.fatal( + "The 'ai' example is not compatible with the Solid frontend.", + ); + process.exit(1); + } +} diff --git a/apps/cli/src/utils/compatibility.ts b/apps/cli/src/utils/compatibility.ts new file mode 100644 index 0000000..3f488d4 --- /dev/null +++ b/apps/cli/src/utils/compatibility.ts @@ -0,0 +1,11 @@ +import type { Frontend } from "../types"; + +export const WEB_FRAMEWORKS: readonly Frontend[] = [ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "solid", +] as const; diff --git a/apps/cli/src/utils/generate-reproducible-command.ts b/apps/cli/src/utils/generate-reproducible-command.ts index 36d06e7..7643495 100644 --- a/apps/cli/src/utils/generate-reproducible-command.ts +++ b/apps/cli/src/utils/generate-reproducible-command.ts @@ -34,15 +34,15 @@ export function generateReproducibleCommand(config: ProjectConfig): string { flags.push(`--package-manager ${config.packageManager}`); flags.push(config.install ? "--install" : "--no-install"); - let baseCommand = ""; + let baseCommand = "npx create-better-t-stack@latest"; const pkgManager = config.packageManager; - if (pkgManager === "npm") { - baseCommand = "npx create-better-t-stack@latest"; + if (pkgManager === "bun") { + baseCommand = "bun create better-t-stack@latest"; } else if (pkgManager === "pnpm") { baseCommand = "pnpm create better-t-stack@latest"; - } else if (pkgManager === "bun") { - baseCommand = "bun create better-t-stack@latest"; + } else if (pkgManager === "npm") { + baseCommand = "npx create-better-t-stack@latest"; } const projectPathArg = config.relativePath ? ` ${config.relativePath}` : ""; diff --git a/apps/cli/src/validation.ts b/apps/cli/src/validation.ts index 10db63b..830d8c6 100644 --- a/apps/cli/src/validation.ts +++ b/apps/cli/src/validation.ts @@ -1,6 +1,5 @@ import path from "node:path"; import { consola } from "consola"; -import { WEB_FRAMEWORKS } from "./constants"; import { type Addons, type API, @@ -17,6 +16,17 @@ import { type Runtime, type WebDeploy, } from "./types"; +import { + coerceBackendPresets, + ensureSingleWebAndNative, + incompatibleFlagsForBackend, + isWebFrontend, + validateAddonsAgainstFrontends, + validateApiFrontendCompatibility, + validateExamplesCompatibility, + validateWebDeployRequiresWebFrontend, + validateWorkersCompatibility, +} from "./utils/compatibility-rules"; export function processAndValidateFlags( options: CLIInput, @@ -126,28 +136,20 @@ export function processAndValidateFlags( const validOptions = options.frontend.filter( (f): f is Frontend => f !== "none", ); - const webFrontends = validOptions.filter((f) => - WEB_FRAMEWORKS.includes(f), - ); - const nativeFrontends = validOptions.filter( - (f) => f === "native-nativewind" || f === "native-unistyles", - ); - - if (webFrontends.length > 1) { - consola.fatal( - "Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid", - ); - process.exit(1); - } - if (nativeFrontends.length > 1) { - consola.fatal( - "Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles", - ); - process.exit(1); - } + ensureSingleWebAndNative(validOptions); config.frontend = validOptions; } } + + if ( + providedFlags.has("api") && + providedFlags.has("frontend") && + config.api && + config.frontend && + config.frontend.length > 0 + ) { + validateApiFrontendCompatibility(config.api, config.frontend); + } if (options.addons && options.addons.length > 0) { if (options.addons.includes("none")) { if (options.addons.length > 1) { @@ -178,32 +180,26 @@ export function processAndValidateFlags( } } - if (config.backend === "convex") { - const incompatibleFlags: string[] = []; - - if (providedFlags.has("auth") && options.auth === true) - incompatibleFlags.push("--auth"); - if (providedFlags.has("database") && options.database !== "none") - incompatibleFlags.push(`--database ${options.database}`); - if (providedFlags.has("orm") && options.orm !== "none") - incompatibleFlags.push(`--orm ${options.orm}`); - if (providedFlags.has("api") && options.api !== "none") - incompatibleFlags.push(`--api ${options.api}`); - if (providedFlags.has("runtime") && options.runtime !== "none") - incompatibleFlags.push(`--runtime ${options.runtime}`); - if (providedFlags.has("dbSetup") && options.dbSetup !== "none") - incompatibleFlags.push(`--db-setup ${options.dbSetup}`); - + if (config.backend === "convex" || config.backend === "none") { + const incompatibleFlags = incompatibleFlagsForBackend( + config.backend, + providedFlags, + options, + ); if (incompatibleFlags.length > 0) { consola.fatal( - `The following flags are incompatible with '--backend convex': ${incompatibleFlags.join( + `The following flags are incompatible with '--backend ${config.backend}': ${incompatibleFlags.join( ", ", )}. Please remove them.`, ); process.exit(1); } - if (providedFlags.has("frontend") && options.frontend) { + if ( + config.backend === "convex" && + providedFlags.has("frontend") && + options.frontend + ) { const incompatibleFrontends = options.frontend.filter( (f) => f === "solid", ); @@ -217,54 +213,15 @@ export function processAndValidateFlags( } } - config.auth = false; - config.database = "none"; - config.orm = "none"; - config.api = "none"; - config.runtime = "none"; - config.dbSetup = "none"; - config.examples = ["todo"]; - } else if (config.backend === "none") { - const incompatibleFlags: string[] = []; - - if (providedFlags.has("auth") && options.auth === true) - incompatibleFlags.push("--auth"); - if (providedFlags.has("database") && options.database !== "none") - incompatibleFlags.push(`--database ${options.database}`); - if (providedFlags.has("orm") && options.orm !== "none") - incompatibleFlags.push(`--orm ${options.orm}`); - if (providedFlags.has("api") && options.api !== "none") - incompatibleFlags.push(`--api ${options.api}`); - if (providedFlags.has("runtime") && options.runtime !== "none") - incompatibleFlags.push(`--runtime ${options.runtime}`); - if (providedFlags.has("dbSetup") && options.dbSetup !== "none") - incompatibleFlags.push(`--db-setup ${options.dbSetup}`); - if (providedFlags.has("examples") && options.examples) { - const hasNonNoneExamples = options.examples.some((ex) => ex !== "none"); - if (hasNonNoneExamples) { - incompatibleFlags.push("--examples"); - } - } - - if (incompatibleFlags.length > 0) { - consola.fatal( - `The following flags are incompatible with '--backend none': ${incompatibleFlags.join( - ", ", - )}. Please remove them.`, - ); - process.exit(1); - } - - config.auth = false; - config.database = "none"; - config.orm = "none"; - config.api = "none"; - config.runtime = "none"; - config.dbSetup = "none"; - config.examples = []; + coerceBackendPresets(config); } - if (config.orm === "mongoose" && config.database !== "mongodb") { + if ( + providedFlags.has("orm") && + providedFlags.has("database") && + config.orm === "mongoose" && + config.database !== "mongodb" + ) { consola.fatal( "Mongoose ORM requires MongoDB database. Please use '--database mongodb' or choose a different ORM.", ); @@ -272,6 +229,8 @@ export function processAndValidateFlags( } if ( + providedFlags.has("database") && + providedFlags.has("orm") && config.database === "mongodb" && config.orm && config.orm !== "mongoose" && @@ -283,28 +242,50 @@ export function processAndValidateFlags( process.exit(1); } - if (config.orm === "drizzle" && config.database === "mongodb") { + if ( + providedFlags.has("orm") && + providedFlags.has("database") && + config.orm === "drizzle" && + config.database === "mongodb" + ) { consola.fatal( "Drizzle ORM does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.", ); process.exit(1); } - if (config.database && config.database !== "none" && config.orm === "none") { + if ( + providedFlags.has("database") && + providedFlags.has("orm") && + config.database && + config.database !== "none" && + config.orm === "none" + ) { consola.fatal( "Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', or '--orm mongoose'.", ); process.exit(1); } - if (config.orm && config.orm !== "none" && config.database === "none") { + if ( + providedFlags.has("orm") && + providedFlags.has("database") && + config.orm && + config.orm !== "none" && + config.database === "none" + ) { consola.fatal( "ORM selection requires a database. Please choose a database or set '--orm none'.", ); process.exit(1); } - if (config.auth && config.database === "none") { + if ( + providedFlags.has("auth") && + providedFlags.has("database") && + config.auth && + config.database === "none" + ) { consola.fatal( "Authentication requires a database. Please choose a database or set '--no-auth'.", ); @@ -312,6 +293,8 @@ export function processAndValidateFlags( } if ( + providedFlags.has("dbSetup") && + providedFlags.has("database") && config.dbSetup && config.dbSetup !== "none" && config.database === "none" @@ -322,35 +305,60 @@ export function processAndValidateFlags( process.exit(1); } - if (config.dbSetup === "turso" && config.database !== "sqlite") { + if ( + providedFlags.has("dbSetup") && + (config.database ? providedFlags.has("database") : true) && + config.dbSetup === "turso" && + config.database !== "sqlite" + ) { consola.fatal( "Turso setup requires SQLite database. Please use '--database sqlite' or choose a different setup.", ); process.exit(1); } - if (config.dbSetup === "neon" && config.database !== "postgres") { + if ( + providedFlags.has("dbSetup") && + (config.database ? providedFlags.has("database") : true) && + config.dbSetup === "neon" && + config.database !== "postgres" + ) { consola.fatal( "Neon setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.", ); process.exit(1); } - if (config.dbSetup === "prisma-postgres" && config.database !== "postgres") { + if ( + providedFlags.has("dbSetup") && + (config.database ? providedFlags.has("database") : true) && + config.dbSetup === "prisma-postgres" && + config.database !== "postgres" + ) { consola.fatal( "Prisma PostgreSQL setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.", ); process.exit(1); } - if (config.dbSetup === "mongodb-atlas" && config.database !== "mongodb") { + if ( + providedFlags.has("dbSetup") && + (config.database ? providedFlags.has("database") : true) && + config.dbSetup === "mongodb-atlas" && + config.database !== "mongodb" + ) { consola.fatal( "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup.", ); process.exit(1); } - if (config.dbSetup === "supabase" && config.database !== "postgres") { + if ( + providedFlags.has("dbSetup") && + (config.database ? providedFlags.has("database") : true) && + config.dbSetup === "supabase" && + config.database !== "postgres" + ) { consola.fatal( "Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.", ); @@ -358,144 +366,61 @@ export function processAndValidateFlags( } if (config.dbSetup === "d1") { - if (config.database !== "sqlite") { - consola.fatal( - "Cloudflare D1 setup requires SQLite database. Please use '--database sqlite' or choose a different setup.", - ); - process.exit(1); + if ( + (providedFlags.has("dbSetup") && providedFlags.has("database")) || + (providedFlags.has("dbSetup") && !config.database) + ) { + if (config.database !== "sqlite") { + consola.fatal( + "Cloudflare D1 setup requires SQLite database. Please use '--database sqlite' or choose a different setup.", + ); + process.exit(1); + } } - if (config.runtime !== "workers") { - consola.fatal( - "Cloudflare D1 setup requires the Cloudflare Workers runtime. Please use '--runtime workers' or choose a different setup.", - ); - process.exit(1); + if ( + (providedFlags.has("dbSetup") && providedFlags.has("runtime")) || + (providedFlags.has("dbSetup") && !config.runtime) + ) { + if (config.runtime !== "workers") { + consola.fatal( + "Cloudflare D1 setup requires the Cloudflare Workers runtime. Please use '--runtime workers' or choose a different setup.", + ); + process.exit(1); + } } } - if (config.dbSetup === "docker" && config.database === "sqlite") { + if ( + providedFlags.has("dbSetup") && + providedFlags.has("database") && + config.dbSetup === "docker" && + config.database === "sqlite" + ) { consola.fatal( "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.", ); process.exit(1); } - if (config.dbSetup === "docker" && config.runtime === "workers") { + if ( + providedFlags.has("dbSetup") && + providedFlags.has("runtime") && + config.dbSetup === "docker" && + config.runtime === "workers" + ) { consola.fatal( "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.", ); process.exit(1); } - if ( - providedFlags.has("runtime") && - options.runtime === "workers" && - config.backend && - config.backend !== "hono" - ) { - consola.fatal( - `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.`, - ); - process.exit(1); - } - - if ( - providedFlags.has("backend") && - config.backend && - config.backend !== "hono" && - config.runtime === "workers" - ) { - consola.fatal( - `Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`, - ); - process.exit(1); - } - - if ( - providedFlags.has("runtime") && - options.runtime === "workers" && - config.orm && - config.orm !== "drizzle" && - config.orm !== "none" - ) { - consola.fatal( - `Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`, - ); - process.exit(1); - } - - if ( - providedFlags.has("orm") && - config.orm && - config.orm !== "drizzle" && - config.orm !== "none" && - config.runtime === "workers" - ) { - consola.fatal( - `ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`, - ); - process.exit(1); - } - - if ( - providedFlags.has("runtime") && - options.runtime === "workers" && - config.database === "mongodb" - ) { - consola.fatal( - "Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.", - ); - process.exit(1); - } - - if ( - providedFlags.has("runtime") && - options.runtime === "workers" && - config.dbSetup === "docker" - ) { - consola.fatal( - "Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. 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.", - ); - process.exit(1); - } - - if ( - providedFlags.has("database") && - config.database === "mongodb" && - config.runtime === "workers" - ) { - consola.fatal( - "MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.", - ); - process.exit(1); - } - - if ( - providedFlags.has("db-setup") && - options.dbSetup === "docker" && - config.runtime === "workers" - ) { - consola.fatal( - "Docker setup (--db-setup docker) 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.", - ); - process.exit(1); - } + validateWorkersCompatibility(providedFlags, options, config); const hasWebFrontendFlag = (config.frontend ?? []).some((f) => - WEB_FRAMEWORKS.includes(f), + isWebFrontend(f), ); - - if ( - config.webDeploy && - config.webDeploy !== "none" && - !hasWebFrontendFlag && - providedFlags.has("frontend") - ) { - consola.fatal( - "'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.", - ); - process.exit(1); - } + validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag); return config; } @@ -505,140 +430,20 @@ export function validateConfigCompatibility(config: Partial) { const effectiveBackend = config.backend; const effectiveFrontend = config.frontend; const effectiveApi = config.api; - const effectiveRuntime = config.runtime; - if (effectiveRuntime === "workers" && effectiveBackend !== "hono") { - consola.fatal( - `Cloudflare Workers runtime is only supported with Hono backend. Current backend: ${effectiveBackend}. Please use a different runtime or change to Hono backend.`, - ); - process.exit(1); - } - - const effectiveOrm = config.orm; - if ( - effectiveRuntime === "workers" && - effectiveOrm !== "drizzle" && - effectiveOrm !== "none" - ) { - consola.fatal( - `Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Current ORM: ${effectiveOrm}. Please use a different runtime or change to Drizzle ORM or no ORM.`, - ); - process.exit(1); - } - - if (effectiveRuntime === "workers" && effectiveDatabase === "mongodb") { - consola.fatal( - "Cloudflare Workers runtime is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.", - ); - process.exit(1); - } - - if (effectiveRuntime === "workers" && config.dbSetup === "docker") { - consola.fatal( - "Cloudflare Workers runtime is not compatible with Docker setup. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use a different runtime or change to D1 database setup.", - ); - process.exit(1); - } - - const includesNuxt = effectiveFrontend?.includes("nuxt"); - const includesSvelte = effectiveFrontend?.includes("svelte"); - const includesSolid = effectiveFrontend?.includes("solid"); - - if ( - (includesNuxt || includesSvelte || includesSolid) && - effectiveApi === "trpc" - ) { - consola.fatal( - `tRPC API is not supported with '${ - includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid" - }' frontend. Please use --api orpc or --api none or remove '${ - includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid" - }' from --frontend.`, - ); - process.exit(1); - } + validateApiFrontendCompatibility(effectiveApi, effectiveFrontend); if (config.addons && config.addons.length > 0) { - const webSpecificAddons = ["pwa", "tauri"]; - const hasWebSpecificAddons = config.addons.some((addon) => - webSpecificAddons.includes(addon), - ); - const hasCompatibleWebFrontend = effectiveFrontend?.some((f) => { - const isPwaCompatible = - f === "tanstack-router" || - f === "react-router" || - f === "solid" || - f === "next"; - const isTauriCompatible = - f === "tanstack-router" || - f === "react-router" || - f === "nuxt" || - f === "svelte" || - f === "solid" || - f === "next"; - - if (config.addons?.includes("pwa") && config.addons?.includes("tauri")) { - return isPwaCompatible && isTauriCompatible; - } - if (config.addons?.includes("pwa")) { - return isPwaCompatible; - } - if (config.addons?.includes("tauri")) { - return isTauriCompatible; - } - return true; - }); - - if (hasWebSpecificAddons && !hasCompatibleWebFrontend) { - let incompatibleReason = "Selected frontend is not compatible."; - if (config.addons.includes("pwa")) { - incompatibleReason = - "PWA requires tanstack-router, react-router, next, or solid."; - } - if (config.addons.includes("tauri")) { - incompatibleReason = - "Tauri requires tanstack-router, react-router, nuxt, svelte, solid, or next."; - } - consola.fatal( - `Incompatible addon/frontend combination: ${incompatibleReason}`, - ); - process.exit(1); - } - + validateAddonsAgainstFrontends(config.addons, effectiveFrontend); config.addons = [...new Set(config.addons)]; } - if ( - config.examples && - config.examples.length > 0 && - !config.examples.includes("none") - ) { - if ( - config.examples.includes("todo") && - effectiveBackend !== "convex" && - effectiveBackend !== "none" && - effectiveDatabase === "none" - ) { - consola.fatal( - "The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.", - ); - process.exit(1); - } - - if (config.examples.includes("ai") && effectiveBackend === "elysia") { - consola.fatal( - "The 'ai' example is not compatible with the Elysia backend.", - ); - process.exit(1); - } - - if (config.examples.includes("ai") && includesSolid) { - consola.fatal( - "The 'ai' example is not compatible with the Solid frontend.", - ); - process.exit(1); - } - } + validateExamplesCompatibility( + config.examples ?? [], + effectiveBackend, + effectiveDatabase, + effectiveFrontend ?? [], + ); } export function getProvidedFlags(options: CLIInput): Set { diff --git a/apps/web/content/docs/analytics.mdx b/apps/web/content/docs/analytics.mdx new file mode 100644 index 0000000..e4967c5 --- /dev/null +++ b/apps/web/content/docs/analytics.mdx @@ -0,0 +1,55 @@ +--- +title: Analytics & Telemetry +description: What we collect, how to disable it, and where to view aggregated insights +--- + +## What is collected + +On successful project creation, the CLI sends a single event (`project_created`) with: + +- Selected options (stack choices): `frontend`, `backend`, `runtime`, `database`, `orm`, `api`, `auth`, `addons`, `examples`, `dbSetup`, `webDeploy`, `packageManager` +- Environment data: `cli_version`, `node_version`, `platform` +- A random session id: `distinct_id` like `cli_` +- IP removed: `$ip: null` + +Not collected: + +- Project name, path, or file contents (explicitly omitted) +- Secrets or environment variables from your machine + + + +## Disable telemetry + +Telemetry is enabled by default. To disable: + +```bash +# Disable for a single run +BTS_TELEMETRY_DISABLED=1 bun create better-t-stack@latest +``` + +Add `export BTS_TELEMETRY_DISABLED=1` to your shell profile to make it permanent. + +## Where to view analytics + +- Charts: [`/analytics`](/analytics) +- Raw JSON snapshot: `https://r2.amanv.dev/analytics-data.json` +- CSV export: `https://r2.amanv.dev/export.csv` + +Notes: + +- Aggregates are periodically regenerated from incoming events +- Raw data is not publicly exposed; the `/analytics` page presents only summary statistics + +## Full transparency + +Single event per scaffold; randomized id; no IP or project identifiers. See source code below. + +If in doubt, set `BTS_TELEMETRY_DISABLED=1` and proceed. You can still use all CLI features. + +## Source code + +- CLI event sender: [`apps/cli/src/utils/analytics.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/analytics.ts) +- Telemetry toggle logic: [`apps/cli/src/utils/telemetry.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/telemetry.ts) +- Aggregation script (builds the charts data): [`apps/web/scripts/generate-analytics.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/web/scripts/generate-analytics.ts) + diff --git a/apps/web/content/docs/bts-config.mdx b/apps/web/content/docs/bts-config.mdx new file mode 100644 index 0000000..ab2c436 --- /dev/null +++ b/apps/web/content/docs/bts-config.mdx @@ -0,0 +1,54 @@ +--- +title: bts.jsonc +description: What bts.jsonc does and why it matters +--- + +## What is it? + +`bts.jsonc` is a small config file written to your project root when you create a project. It captures the stack choices you selected (frontend, backend, API, DB/ORM, auth, addons, etc.). The file uses JSONC (JSON with comments) and includes a schema for editor hints. + +Where: `./bts.jsonc` + +## Why it exists + +- Required for the `add` command to detect your current stack +- Helps validate compatibility and pre‑fill sensible defaults + +If `bts.jsonc` is missing, the `add` command cannot run because the project cannot be detected. + +## Safe to delete (with a caveat) + +It’s safe to delete for normal development; the generated code in `apps/*` and `packages/*` remains the source of truth. However, if you plan to use the `add` command later, you must keep `bts.jsonc` (or recreate it) so the CLI can detect your project. + +## Format + +The file is JSONC with comments enabled and includes a `$schema` URL for tooling. + +```jsonc +// Better-T-Stack configuration file +// safe to delete +{ + "$schema": "https://r2.better-t-stack.dev/schema.json", + "version": "x.y.z", + "createdAt": "2025-01-01T00:00:00.000Z", + "frontend": ["tanstack-router"], + "backend": "hono", + "runtime": "bun", + "database": "sqlite", + "orm": "drizzle", + "api": "trpc", + "auth": true, + "addons": ["turborepo"], + "examples": [], + "dbSetup": "none", + "webDeploy": "none", + "packageManager": "bun" +} +``` + +Notes: +- Values mirror what you selected during project creation +- The file may be updated when you run `add` (e.g., addons or webDeploy) + +See also: [`add` command](/docs/cli/add) + diff --git a/apps/web/content/docs/cli-commands.mdx b/apps/web/content/docs/cli-commands.mdx index 8c075d8..408a6cb 100644 --- a/apps/web/content/docs/cli-commands.mdx +++ b/apps/web/content/docs/cli-commands.mdx @@ -3,6 +3,32 @@ title: CLI Commands description: Complete reference for all Better-T-Stack CLI commands and options --- +## Quick Usage + + + + ```bash + # Interactive setup + bun create better-t-stack@latest + + # With project name + bun create better-t-stack@latest my-project + ``` + + + ```bash + pnpm create better-t-stack@latest + pnpm create better-t-stack@latest my-project + ``` + + + ```bash + npx create-better-t-stack@latest + npx create-better-t-stack@latest my-project + ``` + + + ## Commands Overview | Command | Description | @@ -21,19 +47,39 @@ The primary command for creating new Better-T-Stack projects. ### Basic Usage -```bash -# Interactive setup -npx create-better-t-stack@latest + + + ```bash + # Interactive setup + bun create better-t-stack@latest -# With project name -npx create-better-t-stack@latest my-project + # With project name + bun create better-t-stack@latest my-project -# With specific directory -npx create-better-t-stack@latest ./path/to/project + # With specific directory + bun create better-t-stack@latest ./path/to/project -# Use current directory -npx create-better-t-stack@latest . -``` + # Use current directory + bun create better-t-stack@latest . + ``` + + + ```bash + pnpm create better-t-stack@latest + pnpm create better-t-stack@latest my-project + pnpm create better-t-stack@latest ./path/to/project + pnpm create better-t-stack@latest . + ``` + + + ```bash + npx create-better-t-stack@latest + npx create-better-t-stack@latest my-project + npx create-better-t-stack@latest ./path/to/project + npx create-better-t-stack@latest . + ``` + + ### Flags Reference @@ -71,13 +117,13 @@ npx create-better-t-stack@latest . # Web + Mobile --frontend tanstack-router native-nativewind -# Multiple frontends ---frontend next solid - # No frontend (API-only) --frontend none ``` +Note: Select at most one web frontend (one of: tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid) and at most one native frontend (one of: native-nativewind, native-unistyles). +Do not combine `none` with other frontend values. + #### Backend Options | Flag | Values | Default | Description | @@ -200,8 +246,13 @@ npx create-better-t-stack@latest . - `biome` - Biome linter/formatter - `husky` - Git hooks with Husky - `starlight` - Documentation site + - `fumadocs` - Fumadocs docs site + - `ultracite` - Markdown/MDX processing + - `oxlint` - Fast linter - `none` - No addons +Note: Do not combine `none` with other addon values. + **Examples:** ```bash --addons turborepo pwa @@ -227,6 +278,8 @@ npx create-better-t-stack@latest . --examples none ``` +Note: Do not combine `none` with other example values. + #### Database Setup Options | Flag | Values | Default | Description | @@ -295,19 +348,39 @@ Add addons or deployment configurations to existing Better-T-Stack projects. ### Usage -```bash -# Interactive addon selection -npx create-better-t-stack@latest add + + + ```bash + # Interactive addon selection + bun create better-t-stack@latest add -# Add specific addons -npx create-better-t-stack@latest add --addons pwa tauri + # Add specific addons + bun create better-t-stack@latest add --addons pwa tauri -# Add deployment -npx create-better-t-stack@latest add --web-deploy workers + # Add deployment + bun create better-t-stack@latest add --web-deploy workers -# Add both -npx create-better-t-stack@latest add --addons biome --web-deploy workers -``` + # Add both + bun create better-t-stack@latest add --addons biome --web-deploy workers + ``` + + + ```bash + pnpm create better-t-stack@latest add + pnpm create better-t-stack@latest add --addons pwa tauri + pnpm create better-t-stack@latest add --web-deploy workers + pnpm create better-t-stack@latest add --addons biome --web-deploy workers + ``` + + + ```bash + npx create-better-t-stack@latest add + npx create-better-t-stack@latest add --addons pwa tauri + npx create-better-t-stack@latest add --web-deploy workers + npx create-better-t-stack@latest add --addons biome --web-deploy workers + ``` + + ### Flags @@ -341,9 +414,23 @@ Display Better-T-Stack project sponsors. ### Usage -```bash -npx create-better-t-stack@latest sponsors -``` + + + ```bash + bun create better-t-stack@latest sponsors + ``` + + + ```bash + pnpm create better-t-stack@latest sponsors + ``` + + + ```bash + npx create-better-t-stack@latest sponsors + ``` + + Shows a list of project sponsors with their GitHub profiles and websites. @@ -355,9 +442,23 @@ Open Better-T-Stack documentation in your default browser. ### Usage -```bash -npx create-better-t-stack@latest docs -``` + + + ```bash + bun create better-t-stack@latest docs + ``` + + + ```bash + pnpm create better-t-stack@latest docs + ``` + + + ```bash + npx create-better-t-stack@latest docs + ``` + + Opens: https://better-t-stack.dev/docs @@ -369,9 +470,23 @@ Open the web-based interactive stack builder. ### Usage -```bash -npx create-better-t-stack@latest builder -``` + + + ```bash + bun create better-t-stack@latest builder + ``` + + + ```bash + pnpm create better-t-stack@latest builder + ``` + + + ```bash + npx create-better-t-stack@latest builder + ``` + + Opens: https://better-t-stack.dev/new @@ -384,7 +499,7 @@ The web builder provides a visual interface for configuring your stack and gener ### Full-Stack Web Application ```bash -npx create-better-t-stack@latest my-webapp \ +bun create better-t-stack@latest my-webapp \ --frontend tanstack-router \ --backend hono \ --runtime bun \ @@ -400,7 +515,7 @@ npx create-better-t-stack@latest my-webapp \ ### Mobile + Web Application ```bash -npx create-better-t-stack@latest my-mobile-app \ +bun create better-t-stack@latest my-mobile-app \ --frontend tanstack-router native-nativewind \ --backend hono \ --database sqlite \ @@ -413,7 +528,7 @@ npx create-better-t-stack@latest my-mobile-app \ ### Cloudflare Workers Project ```bash -npx create-better-t-stack@latest my-workers-app \ +bun create better-t-stack@latest my-workers-app \ --frontend tanstack-router \ --backend hono \ --runtime workers \ @@ -426,7 +541,7 @@ npx create-better-t-stack@latest my-workers-app \ ### API-Only Project ```bash -npx create-better-t-stack@latest my-api \ +bun create better-t-stack@latest my-api \ --frontend none \ --backend fastify \ --runtime node \ @@ -440,10 +555,10 @@ npx create-better-t-stack@latest my-api \ ```bash # Use all defaults with immediate setup -npx create-better-t-stack@latest my-project --yes +bun create better-t-stack@latest my-project --yes # Convex full-stack (automatically configures compatible options) -npx create-better-t-stack@latest my-convex-app --backend convex +bun create better-t-stack@latest my-convex-app --backend convex ``` --- @@ -452,11 +567,29 @@ npx create-better-t-stack@latest my-convex-app --backend convex Some options have compatibility requirements: -- **Cloudflare Workers**: Only with `--backend hono`, `--orm drizzle`, `--runtime workers` +- **Cloudflare Workers**: Only with `--backend hono`, `--orm drizzle` or `--orm none`, `--runtime workers` - **MongoDB**: Requires `--orm mongoose` or `--orm prisma` -- **Convex**: Incompatible with custom database/ORM/API options +- **Convex**: Forces `--no-auth`, `--database none`, `--orm none`, `--api none`, `--runtime none`, `--db-setup none`; sets `--examples todo`; Solid frontend is not supported with Convex - **PWA**: Requires compatible web frontend - **Tauri**: Requires compatible web frontend - **AI Examples**: Not compatible with `--backend elysia` or `--frontend solid` +- **tRPC + Nuxt/Svelte/Solid**: Use `--api orpc` (tRPC is not supported with `nuxt`, `svelte`, or `solid`) +- **Todo Example**: Requires a database when a backend (other than Convex) is selected +- **Web Deploy**: `--web-deploy workers` requires selecting a web frontend +- **Examples with `--api none`**: Cannot select examples when `--api none` (unless `--backend convex`) +- **Runtime `none`**: Only allowed with `--backend convex` or `--backend none` +- **Mongoose ORM**: Requires `--database mongodb` +- **Drizzle ORM**: Not compatible with `--database mongodb` +- **Database requires ORM**: Selecting a database requires a non-`none` ORM +- **ORM requires database**: Selecting a non-`none` ORM requires a database +- **Auth requires database**: `--auth` requires a non-`none` database +- **DB Setup constraints**: + - `--db-setup turso` → requires `--database sqlite` + - `--db-setup neon` → requires `--database postgres` + - `--db-setup prisma-postgres` → requires `--database postgres` + - `--db-setup mongodb-atlas` → requires `--database mongodb` + - `--db-setup supabase` → requires `--database postgres` + - `--db-setup d1` → requires `--database sqlite` and `--runtime workers` + - `--db-setup docker` → not compatible with `--database sqlite` or `--runtime workers` The CLI will validate compatibility and show helpful error messages for invalid combinations. diff --git a/apps/web/content/docs/cli/add.mdx b/apps/web/content/docs/cli/add.mdx new file mode 100644 index 0000000..292f2f1 --- /dev/null +++ b/apps/web/content/docs/cli/add.mdx @@ -0,0 +1,35 @@ +--- +title: add +description: Add addons or deployment to an existing project +--- + +## Usage + + + + ```bash + bun create better-t-stack@latest add [options] + ``` + + + ```bash + pnpm create better-t-stack@latest add [options] + ``` + + + ```bash + npx create-better-t-stack@latest add [options] + ``` + + + +### Options + +- `--addons` Multiple values +- `--web-deploy` One of: `workers`, `none` +- `--project-dir` Path +- `--install` / `--no-install` +- `--package-manager` One of: `bun`, `pnpm`, `npm` + +See also: [Compatibility](/docs/compatibility) + diff --git a/apps/web/content/docs/cli/builder.mdx b/apps/web/content/docs/cli/builder.mdx new file mode 100644 index 0000000..39df6b0 --- /dev/null +++ b/apps/web/content/docs/cli/builder.mdx @@ -0,0 +1,25 @@ +--- +title: builder +description: Open the web-based stack builder +--- + +## Usage + + + + ```bash + bun create better-t-stack@latest builder + ``` + + + ```bash + pnpm create better-t-stack@latest builder + ``` + + + ```bash + npx create-better-t-stack@latest builder + ``` + + + diff --git a/apps/web/content/docs/cli/docs.mdx b/apps/web/content/docs/cli/docs.mdx new file mode 100644 index 0000000..f5902b2 --- /dev/null +++ b/apps/web/content/docs/cli/docs.mdx @@ -0,0 +1,25 @@ +--- +title: docs +description: Open the documentation +--- + +## Usage + + + + ```bash + bun create better-t-stack@latest docs + ``` + + + ```bash + pnpm create better-t-stack@latest docs + ``` + + + ```bash + npx create-better-t-stack@latest docs + ``` + + + diff --git a/apps/web/content/docs/cli/index.mdx b/apps/web/content/docs/cli/index.mdx new file mode 100644 index 0000000..94774e6 --- /dev/null +++ b/apps/web/content/docs/cli/index.mdx @@ -0,0 +1,23 @@ +--- +title: CLI +description: Command reference index +--- + + + + Create a new Better‑T‑Stack project (prompts or --yes) + + + Add addons or deployment to an existing project + + + Open the web-based Stack Builder + + + Open documentation + + + View project sponsors + + + diff --git a/apps/web/content/docs/cli/init.mdx b/apps/web/content/docs/cli/init.mdx new file mode 100644 index 0000000..eb6bd22 --- /dev/null +++ b/apps/web/content/docs/cli/init.mdx @@ -0,0 +1,35 @@ +--- +title: init +description: Create a new Better-T-Stack project +--- + +## Usage + + + + ```bash + bun create better-t-stack@latest [project-directory] + ``` + + + ```bash + pnpm create better-t-stack@latest [project-directory] + ``` + + + ```bash + npx create-better-t-stack@latest [project-directory] + ``` + + + +You can pass `.` to use the current directory. + +### Skip prompts + +```bash +bun create better-t-stack@latest my-app --yes +``` + +See also: [Compatibility](/docs/compatibility) + diff --git a/apps/web/content/docs/cli/meta.json b/apps/web/content/docs/cli/meta.json new file mode 100644 index 0000000..fd5ecda --- /dev/null +++ b/apps/web/content/docs/cli/meta.json @@ -0,0 +1,5 @@ +{ + "title": "CLI", + "defaultOpen": true, + "pages": ["index", "init", "add", "builder", "docs", "sponsors"] +} diff --git a/apps/web/content/docs/cli/sponsors.mdx b/apps/web/content/docs/cli/sponsors.mdx new file mode 100644 index 0000000..b44f1cc --- /dev/null +++ b/apps/web/content/docs/cli/sponsors.mdx @@ -0,0 +1,25 @@ +--- +title: sponsors +description: View project sponsors +--- + +## Usage + + + + ```bash + bun create better-t-stack@latest sponsors + ``` + + + ```bash + pnpm create better-t-stack@latest sponsors + ``` + + + ```bash + npx create-better-t-stack@latest sponsors + ``` + + + diff --git a/apps/web/content/docs/compatibility.mdx b/apps/web/content/docs/compatibility.mdx new file mode 100644 index 0000000..4eddcfe --- /dev/null +++ b/apps/web/content/docs/compatibility.mdx @@ -0,0 +1,29 @@ +--- +title: Compatibility +description: Valid and invalid combinations across frontend, backend, runtime, database, and addons +--- + +## Rules + +- **Convex backend**: Disables authentication, database, ORM, and API options +- **Backend `none`**: Forces API, ORM, database, authentication, and runtime to `none`; disables examples +- **Frontend `none`**: Backend-only project; PWA/Tauri/examples may be disabled +- **API `none`**: No tRPC/oRPC setup; use framework-native APIs +- **Database `none`**: Disables ORM and authentication +- **ORM `none`**: No ORM setup; manage DB manually +- **Runtime `none`**: Only with Convex backend or when backend is `none` + +## Cloudflare Workers + +- Backend: `hono` only +- Database: `sqlite` with Cloudflare D1 +- ORM: `drizzle` (or none) +- Not compatible with MongoDB + +## Framework Notes + +- SvelteKit, Nuxt, and SolidJS frontends are only compatible with `orpc` API layer +- PWA addon requires React (TanStack Router/React Router) or SolidJS +- Tauri addon requires React (TanStack Router/React Router), Nuxt, SvelteKit, SolidJS, or Next.js +- AI example is not compatible with Elysia backend or SolidJS frontend + diff --git a/apps/web/content/docs/contributing.mdx b/apps/web/content/docs/contributing.mdx new file mode 100644 index 0000000..ec46e4b --- /dev/null +++ b/apps/web/content/docs/contributing.mdx @@ -0,0 +1,94 @@ +--- +title: Contributing +description: How to set up your environment and contribute changes +--- + +## Overview + +This project is a monorepo with two main apps: + +- CLI: `apps/cli` +- Documentation site: `apps/web` + +> Note: Planning to add a new feature? Please open an issue first to confirm it aligns with the project's future goals. We don't want you to spend time on work that might not land. + +## Setup + +### Prerequisites + +- Node.js 20+ +- Bun (recommended) +- Git + +### Install + +```bash +git clone https://github.com/AmanVarshney01/create-better-t-stack.git +cd create-better-t-stack +bun install +``` + +## Develop the CLI + +```bash +cd apps/cli +# optional global link for testing anywhere +bun link +# run in watch mode +bun dev:cli +``` + +## Develop the Docs + +```bash +# from repo root +bun dev:web +``` + +## Contribution Flow + +1. Open an issue/discussion before starting major work +2. Create a feature branch +3. Make changes following existing code style +4. Update docs as needed +5. Test and format + +```bash +# CLI +cd apps/cli && bun dev:cli + +# Web +bun dev:web + +# Format +bun run format + +# Type checks +bun check +``` + +6. (CLI changes) Add a changeset + +```bash +bunx changeset select create-better-t-stack +# Choose 'patch' for small fixes, 'minor' for features +# Never choose 'major' +``` + +7. Commit and push + +```bash +git add . +git commit -m "feat(web): ..." # or fix(cli): ... +git push origin +``` + +8. Open a Pull Request and link any related issues + +## Help + +- Issues and Discussions on GitHub +- Discord: https://discord.gg/ZYsbjpDaM5 + +See full contributor guide in the repository: `.github/CONTRIBUTING.md`. + diff --git a/apps/web/content/docs/faq.mdx b/apps/web/content/docs/faq.mdx index d6d5e21..7b93952 100644 --- a/apps/web/content/docs/faq.mdx +++ b/apps/web/content/docs/faq.mdx @@ -1,334 +1,46 @@ --- title: Frequently Asked Questions -description: Common questions and answers about Better-T-Stack CLI +description: Short answers to common beginner questions --- -## General Questions +## General -### What is Better-T-Stack? +### What is Better‑T‑Stack? +An opinionated CLI that scaffolds full‑stack TypeScript projects (frontend, backend, API, DB/ORM, auth, addons) with a clean monorepo. See the Quick Start on the docs home. -Better-T-Stack is a modern CLI tool that helps you scaffold end-to-end type-safe TypeScript projects. It provides opinionated, production-ready configurations for full-stack applications with support for multiple frontend frameworks, backend frameworks, databases, and deployment options. +### Do I need to install anything globally? +No. Run the CLI directly with your package manager. See Quick Start and the per‑command pages under CLI. -### How is Better-T-Stack different from other scaffolding tools? +### Which package manager can I use? +`npm`, `pnpm`, or `bun` (all supported). -- **End-to-End Type Safety**: TypeScript across your entire stack with proper type sharing -- **Modern Stack Focus**: Latest versions of popular frameworks and tools -- **Production Ready**: Configurations used in real production applications -- **Highly Customizable**: Mix and match technologies based on your needs -- **Monorepo Structure**: Organized project structure with shared packages -- **Database Integration**: Built-in database setup and ORM configuration -- **Authentication**: Integrated auth with Better-Auth -- **Multi-Platform**: Web, mobile, and desktop app support in one project +### What Node.js version is required? +Node.js 18+ (LTS recommended). -### Is Better-T-Stack free to use? +### Can I use this with an existing project? +The CLI is for new projects. You can migrate gradually or use `add` to extend a Better‑T‑Stack project. -Yes, Better-T-Stack is completely free and open-source under the MIT license. You can use it for personal and commercial projects without any restrictions. +### Where do generated files live? +See Project Structure for high‑level layouts (server‑based vs. Convex, optional web/native). -### Do I need to know all these technologies to use Better-T-Stack? +## Choosing options -No! Better-T-Stack is designed to help you learn modern full-stack development. Each generated project includes: -- Comprehensive README with setup instructions -- Example code and patterns -- TypeScript for better developer experience -- Best practices and folder structure +### Does the CLI recommend a stack? +No. Pick what fits your needs. The CLI validates compatibility. See CLI (per command) and Compatibility for rules. ---- +### I’m unsure between tRPC and oRPC / Drizzle and Prisma +See Compatibility for guidance and constraints. Both pairs work well; choose based on team and hosting needs. -## Installation & Setup +## Common issues -### Which package manager should I use? - -You can use any of the major package managers: -- **npm**: Most widely supported, comes with Node.js -- **pnpm**: Faster installs, better disk space efficiency -- **bun**: All-in-one runtime, extremely fast - -For monorepo projects, we recommend **pnpm** for its excellent workspace support. - -### Do I need to install the CLI globally? - -No, you can use npx to run the latest version without installation: - -```bash -npx create-better-t-stack@latest my-project -``` - -This ensures you always use the latest version with the newest features and bug fixes. - -### What Node.js version do I need? - -Better-T-Stack requires **Node.js 18 or higher**. We recommend using the latest LTS version for the best experience. - -### Can I use Better-T-Stack with existing projects? - -Better-T-Stack is designed for new projects. For existing projects, you can: -1. Create a new Better-T-Stack project -2. Gradually migrate your code -3. Use the `add` command to add specific features to existing Better-T-Stack projects - ---- - -## Configuration & Stack Choices - -### Can I change my stack choices after creating a project? - -Some changes are possible: -- **Easy**: Add addons, examples, or deployment configurations using the `add` command -- **Medium**: Switch between compatible ORMs or databases (requires manual migration) -- **Hard**: Change frontend/backend frameworks (requires significant refactoring) - -It's best to plan your stack carefully during initial setup. - -### What's the difference between tRPC and oRPC? - -- **tRPC**: End-to-end type safety with TypeScript inference, great for TypeScript-only projects -- **oRPC**: OpenAPI-compatible type-safe APIs, better for teams using multiple languages or requiring OpenAPI specs - -Both provide excellent type safety, choose based on your team's needs. - -### Should I use Drizzle or Prisma? - -- **Drizzle**: TypeScript-first, lightweight, great for edge deployments, SQL-like syntax -- **Prisma**: Feature-rich, mature ecosystem, great tooling, GraphQL-like schema - -Choose Drizzle for modern TypeScript projects and Prisma for feature-rich applications. - -### What's the recommended stack for beginners? - -For beginners, we recommend: -```bash -npx create-better-t-stack@latest my-first-project \ - --frontend tanstack-router \ - --backend hono \ - --database sqlite \ - --orm drizzle \ - --auth \ - --addons turborepo biome -``` - -This provides a simple but powerful full-stack setup that's easy to understand and deploy. - ---- - -## Compatibility Questions - -### Can I use MongoDB with Drizzle? - -No, Drizzle doesn't support MongoDB. For MongoDB, use: -- **Prisma ORM**: Full ORM support for MongoDB -- **Mongoose**: Traditional MongoDB object modeling - -### Why can't I use tRPC with Nuxt/SvelteKit/SolidJS? - -tRPC is primarily designed for React ecosystems. For these frameworks, use: -- **oRPC**: Provides similar type safety with broader framework support -- **None**: Use the framework's built-in API capabilities - -### Can I use Cloudflare Workers with any backend? - -Cloudflare Workers runtime only supports: -- **Backend**: Hono only -- **Database**: SQLite with Cloudflare D1 -- **ORM**: Drizzle only - -This is due to the serverless nature and limitations of the Workers environment. - -### Which addons work with which frontends? - -| Addon | Compatible Frontends | -|-------|---------------------| -| PWA | TanStack Router, React Router, SolidJS, Next.js | -| Tauri | TanStack Router, React Router, Nuxt, SvelteKit, SolidJS, Next.js | -| Turborepo | All frontends | -| Biome | All frontends | -| Husky | All frontends | -| Starlight | All frontends | - ---- - -## Database & Hosting - -### What database should I choose for production? - -**For small to medium applications:** -- **SQLite + Turso**: Excellent performance, easy scaling -- **PostgreSQL + Neon**: Serverless PostgreSQL, great for startups - -**For large applications:** -- **PostgreSQL + Supabase**: Full backend-as-a-service -- **MongoDB + Atlas**: NoSQL flexibility with managed hosting - -### Do I need Docker for development? - -Docker is optional and only required for: -- **Database Setup**: If you choose `--db-setup docker` -- **Local Development**: Some setups like Supabase local development - -Many database options (Turso, Neon, MongoDB Atlas) don't require Docker. - -### How do I deploy my Better-T-Stack application? - -Better-T-Stack projects are configured for easy deployment: - -**Frontend:** -- **Vercel**: Zero-config deployment for Next.js, React apps -- **Netlify**: Static site deployment -- **Cloudflare Workers**: Edge deployment with `--web-deploy workers` - -**Backend:** -- **Railway**: Easy backend deployment -- **Fly.io**: Global application deployment -- **Cloudflare Workers**: Serverless edge deployment - ---- - -## Development & Troubleshooting - -### My project won't start after creation. What should I do? - -1. **Check Node.js version**: Ensure you're using Node.js 18+ -2. **Install dependencies**: Run `npm install` in the project directory -3. **Check environment variables**: Copy `.env.example` to `.env` and fill in values -4. **Database setup**: Run database migrations if using a database -5. **Check the README**: Each project includes detailed setup instructions - -### I'm getting TypeScript errors. How do I fix them? - -1. **Install dependencies**: Ensure all packages are installed -2. **Restart TypeScript server**: In VS Code, use Ctrl/Cmd + Shift + P → "TypeScript: Restart TS Server" -3. **Check imports**: Ensure all imports are correct and packages are installed -4. **Update types**: Run `npm run check-types` to see detailed errors - -### How do I update dependencies in my project? - -```bash -# Check for updates -npx taze -r - -# Update all dependencies -npm update - -# Or with other package managers -pnpm update -bun update -``` - -### Can I use Better-T-Stack with VS Code? - -Yes! Better-T-Stack projects work excellently with VS Code. We recommend these extensions: -- TypeScript and JavaScript Language Features (built-in) -- Tailwind CSS IntelliSense -- Prisma or Drizzle Kit extensions -- ESLint (if using Biome addon) - -### My mobile app won't connect to the backend. What's wrong? - -This is a common issue with Expo and local development: - -1. **Check environment variables**: Update `EXPO_PUBLIC_SERVER_URL` in `apps/native/.env` -2. **Use your local IP**: Replace `localhost` with your computer's IP address -3. **Check firewall**: Ensure your firewall allows connections on port 3000 -4. **Use tunnel**: Consider using `npx expo start --tunnel` - ---- - -## Advanced Usage - -### Can I customize the generated templates? - -Currently, Better-T-Stack doesn't support custom templates, but you can: -1. Fork the repository and modify templates -2. Create a feature request for specific customizations -3. Modify the generated project after creation - -### How do I contribute to Better-T-Stack? - -We welcome contributions! Here's how to get started: - -1. **Fork the repository** on GitHub -2. **Clone your fork** locally -3. **Install dependencies**: `pnpm install` -4. **Make your changes** and test them -5. **Submit a pull request** with a clear description - -### Can I use Better-T-Stack in my company/team? - -Absolutely! Better-T-Stack is perfect for: -- **Standardizing project structure** across teams -- **Onboarding new developers** with consistent setup -- **Rapid prototyping** and MVP development -- **Client projects** with proven, production-ready configurations +### My mobile app can’t connect to the backend (Expo) +Set `EXPO_PUBLIC_SERVER_URL` in `apps/native/.env` to your machine IP (not `localhost`), check firewall, or try `npx expo start --tunnel`. ### How do I disable telemetry? +Set `BTS_TELEMETRY_DISABLED=1` (shell env). For one run, prefix the command; to make it permanent, export it in your shell profile. -Better-T-Stack collects anonymous usage data to improve the tool. To disable: +## Getting help -```bash -# Disable for single run -BTS_TELEMETRY_DISABLED=1 npx create-better-t-stack@latest my-app - -# Disable globally -export BTS_TELEMETRY_DISABLED=1 -``` - -Add the export to your shell profile (`.bashrc`, `.zshrc`, etc.) to make it permanent. - ---- - -## Getting Help - -### Where can I get help? - -- **Documentation**: Comprehensive guides at [better-t-stack.dev/docs](https://better-t-stack.dev/docs) -- **GitHub Issues**: Report bugs or request features -- **GitHub Discussions**: Community support and questions -- **Discord/Twitter**: Follow for updates and community discussion - -### How do I report a bug? - -1. **Search existing issues** to avoid duplicates -2. **Create a new issue** with: - - Clear description of the problem - - Steps to reproduce - - Your system information (OS, Node.js version, etc.) - - Generated project configuration - - Error messages or screenshots - -### How do I request a new feature? - -1. **Check existing feature requests** in GitHub Issues -2. **Create a new issue** with the "feature request" label -3. **Describe the feature** and its use case -4. **Explain why** it would benefit the community - -### Is there a community? - -Yes! You can connect with other Better-T-Stack users: -- **GitHub Discussions**: Ask questions and share projects -- **Twitter**: Follow [@AmanVarshney01](https://twitter.com/AmanVarshney01) for updates -- **Show your projects**: Tag us when you build something with Better-T-Stack! - ---- - -## Sponsorship & Support - -### How can I support Better-T-Stack? - -- **⭐ Star the repository** on GitHub -- **🐛 Report bugs** and suggest improvements -- **💰 Sponsor the project** on GitHub Sponsors -- **📢 Share with others** who might find it useful -- **🤝 Contribute code** or documentation - -### Who sponsors Better-T-Stack? - -View current sponsors by running: -```bash -npx create-better-t-stack@latest sponsors -``` - -Or visit: [github.com/sponsors/AmanVarshney01](https://github.com/sponsors/AmanVarshney01) - ---- - -*Still have questions? Feel free to ask in [GitHub Discussions](https://github.com/AmanVarshney01/create-better-t-stack/discussions) or check our [documentation](https://better-t-stack.dev/docs).* +- Docs: Quick Start, CLI, Project Structure, Compatibility +- Ask/Report: GitHub Issues & Discussions +- Community: Discord diff --git a/apps/web/content/docs/index.mdx b/apps/web/content/docs/index.mdx index 45d9571..05acb1f 100644 --- a/apps/web/content/docs/index.mdx +++ b/apps/web/content/docs/index.mdx @@ -1,118 +1,127 @@ --- -title: Getting Started -description: A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations +title: Quick Start +description: Create your first Better-T-Stack project in minutes --- -> ⚠️ **Warning:** Documentation is a work in progress. +## Philosophy -## What is Better-T-Stack? +- With Better T Stack, you freely choose only the parts you want; nothing extra is added. +- Pick your frontend, backend, API layer, database, ORM, auth, addons — or skip any of them. Compatibility is enforced. +- No lock‑in: it’s a generator, not a framework fork. -Better-T-Stack is designed to eliminate the complexity of setting up modern TypeScript projects. Instead of spending hours configuring build tools, type systems, databases, and deployment pipelines, you can get a production-ready project structure in minutes. +## Get Started -### Key Features +### Stack Builder (UI) -- **🏗️ Full-Stack Ready**: Choose from multiple frontend and backend frameworks -- **🔒 End-to-End Type Safety**: TypeScript across your entire application stack -- **🗄️ Database Integration**: Support for SQLite, PostgreSQL, MySQL, and MongoDB -- **🔐 Built-in Authentication**: Email/password auth with Better-Auth -- **📱 Multi-Platform**: Web, mobile (React Native), and desktop (Tauri) support -- **☁️ Deployment Ready**: Configured for modern hosting platforms -- **⚡ Modern Tooling**: Latest versions of your favorite tools and frameworks - -### Supported Technologies - -#### Frontend Frameworks -- **React** with TanStack Router or React Router -- **Next.js** - Full-stack React framework -- **SvelteKit** - Web framework for Svelte -- **Nuxt** - Vue.js framework -- **SolidJS** - Performant reactive framework -- **TanStack Start** - SSR with TanStack Router -- **React Native** - Mobile development with Expo - -#### Backend Frameworks -- **Hono** - Lightweight, ultrafast web framework -- **Express** - Popular Node.js framework -- **Fastify** - Fast, low-overhead framework -- **Elysia** - Type-safe, high-performance framework -- **Convex** - Reactive backend-as-a-service -- **Next.js API Routes** - Full-stack React - -#### Databases & ORMs -- **Databases**: SQLite/Turso, PostgreSQL, MySQL, MongoDB -- **ORMs**: Drizzle (TypeScript-first), Prisma (feature-rich), Mongoose -- **Hosting**: Neon, Supabase, MongoDB Atlas, Cloudflare D1 - -#### API Layers -- **tRPC** - End-to-end type-safe APIs -- **oRPC** - OpenAPI-compatible type-safe APIs - -### Why Choose Better-T-Stack? - -#### Traditional Setup Problems -- ⏰ Hours of configuration and setup -- 🔧 Complex toolchain integration -- 📚 Overwhelming technology choices -- 🐛 Configuration bugs and compatibility issues -- 🏗️ Inconsistent project structures - -#### Better-T-Stack Solutions -- ⚡ **Quick Setup**: Get started in under 2 minutes -- 🎯 **Curated Choices**: Pre-selected, compatible technology combinations -- 📋 **Best Practices**: Industry-standard configurations out of the box -- 🔄 **Consistent Structure**: Standardized monorepo organization -- 🧪 **Battle-Tested**: Configurations used in production applications - -## Quick Example +- Visit [/new](/new) to pick your stack and copy the generated command +- Or open it via: ```bash -# Create a new project -npx create-better-t-stack@latest my-app - -# Choose your stack interactively or use flags -npx create-better-t-stack@latest my-app \ - --frontend tanstack-router \ - --backend hono \ - --database postgres \ - --orm drizzle \ - --auth \ - --addons pwa turborepo +bun create better-t-stack@latest builder ``` -## Project Types +### CLI (prompts) -Better-T-Stack supports various project configurations: +```bash +bun create better-t-stack@latest +``` -### Full-Stack Web Applications -Perfect for modern web apps with React, Vue, or Svelte frontends backed by type-safe APIs. +Follow the interactive prompts to choose your frontend, backend, database, ORM, API layer, and addons. -### Mobile Applications -Build React Native apps with Expo, sharing type definitions with your backend. +Skip prompts and use the default stack: -### Desktop Applications -Create cross-platform desktop apps using Tauri with your web frontend. +```bash +bun create better-t-stack@latest my-app --yes +``` -### API-Only Projects -Build standalone APIs and microservices with your preferred backend framework. +Prefer a visual flow? Use the Stack Builder at [/new](/new) to pick your stack and copy the generated command. You can also open it via: -### Monorepo Projects -Organize multiple applications (web, mobile, API) in a single repository with shared packages. +```bash +bun create better-t-stack@latest builder +``` -## Who Should Use Better-T-Stack? +## Common Setups -- **Indie Developers**: Quickly prototype and build full-stack applications -- **Startups**: Get to market faster with production-ready project structure -- **Teams**: Standardize project setup across your organization -- **Students**: Learn modern full-stack development with best practices -- **Agencies**: Rapidly scaffold client projects with consistent quality +### Default Stack -## What's Next? +```bash +bun create better-t-stack@latest my-webapp \ + --frontend tanstack-router \ + --backend hono \ + --database sqlite \ + --orm drizzle \ + --auth \ + --addons turborepo +``` -Ready to get started? Check out our [Quick Start Guide](/docs/quick-start) to create your first Better-T-Stack project, or explore the [Configuration Options](/docs/frontend) to learn about all available technologies and features. +### Convex + React -### Need Help? +```bash +bun create better-t-stack@latest my-convex-app \ + --frontend tanstack-router \ + --backend convex +``` -- 📖 **Documentation**: Comprehensive guides and references -- 🐛 **Issues**: Report bugs on [GitHub](https://github.com/AmanVarshney01/create-better-t-stack/issues) -- 💬 **Discussions**: Community support and questions -- 🌟 **Star us**: Show support on [GitHub](https://github.com/AmanVarshney01/create-better-t-stack) +### API Only + +```bash +bun create better-t-stack@latest my-api \ + --frontend none \ + --backend fastify \ + --runtime node \ + --database postgres \ + --orm prisma \ + --api trpc +``` + +### Mobile App (Expo) + +```bash +bun create better-t-stack@latest my-native \ + --frontend native-nativewind \ + --backend hono \ + --database sqlite \ + --orm drizzle \ + --auth +``` + +### Empty Monorepo + +```bash +bun create better-t-stack@latest my-workspace \ + --frontend none \ + --backend none +``` + +## Flags Cheat Sheet + +See the full list in the [CLI Reference](/docs/cli). Key flags: + +- `--frontend`: tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, native-nativewind, native-unistyles, none +- `--backend`: hono, express, fastify, elysia, next, convex, none +- `--runtime`: bun, node, workers, none +- `--database`: sqlite, postgres, mysql, mongodb, none +- `--orm`: drizzle, prisma, mongoose, none +- `--api`: trpc, orpc, none +- `--addons`: turborepo, pwa, tauri, biome, husky, starlight, none +- `--examples`: todo, ai, none + +## Next Steps + + + + Flags, usage, and examples for each command + + + See how web/server/native and Convex layouts are generated + + + Valid combinations for backend, runtime, database, ORM, API + + + Required for the add command; safe to delete if you don’t use add + + + Dev setup and contribution flow + + diff --git a/apps/web/content/docs/installation.mdx b/apps/web/content/docs/installation.mdx deleted file mode 100644 index 0dc06be..0000000 --- a/apps/web/content/docs/installation.mdx +++ /dev/null @@ -1,236 +0,0 @@ ---- -title: Installation -description: How to install and set up Better-T-Stack CLI ---- - -## System Requirements - -Before installing Better-T-Stack, ensure your system meets these requirements: - -- **Node.js**: Version 18 or higher -- **Package Manager**: npm, pnpm, or bun -- **Git**: For repository initialization (optional but recommended) - -### Optional Dependencies - -Depending on your project configuration, you may need: - -- **Docker**: For local database development with Docker Compose -- **Rust & System Dependencies**: For Tauri desktop applications -- **Cloudflare CLI**: For Workers deployment - -## Quick Start (Recommended) - -The fastest way to get started is using npx, which runs the latest version without installation: - -```bash -npx create-better-t-stack@latest my-project -``` - -This command will: -1. Download the latest version of the CLI -2. Run the interactive setup wizard -3. Create your project in the `my-project` directory - -## Package Manager Specific Commands - -### npm - -```bash -# Run without installing -npx create-better-t-stack@latest my-project - -# Or install globally -npm install -g create-better-t-stack -create-better-t-stack my-project -``` - -### pnpm - -```bash -# Run without installing (recommended) -pnpm create better-t-stack@latest my-project - -# Or install globally -pnpm add -g create-better-t-stack -create-better-t-stack my-project -``` - -### bun - -```bash -# Run without installing (recommended) -bun create better-t-stack@latest my-project - -# Or install globally -bun add -g create-better-t-stack -create-better-t-stack my-project -``` - -## Global Installation - -If you frequently create new projects, you might want to install the CLI globally: - - - - ```bash - npm install -g create-better-t-stack - ``` - - - ```bash - pnpm add -g create-better-t-stack - ``` - - - ```bash - bun add -g create-better-t-stack - ``` - - - -After global installation, you can run: - -```bash -create-better-t-stack my-project -``` - -## Verification - -Verify your installation by checking the version: - -```bash -# If installed globally -create-better-t-stack --version - -# Or with npx -npx create-better-t-stack@latest --version -``` - -You should see output similar to: - -``` -2.26.3 -``` - -## Development Installation - -For contributing to Better-T-Stack or running the latest development version: - -```bash -# Clone the repository -git clone https://github.com/AmanVarshney01/create-better-t-stack.git -cd create-better-t-stack - -# Install dependencies -pnpm install - -# Build the CLI -cd apps/cli -pnpm build - -# Link for local development -pnpm link --global -``` - -## Troubleshooting - -### Common Issues - -#### Permission Errors (npm) - -If you encounter permission errors with npm global installation: - -```bash -# Use npx instead (recommended) -npx create-better-t-stack@latest my-project - -# Or configure npm to use a different directory -mkdir ~/.npm-global -npm config set prefix '~/.npm-global' -echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc -source ~/.bashrc -``` - -#### Node.js Version Issues - -Ensure you're using Node.js 18 or higher: - -```bash -node --version -``` - -If you need to upgrade Node.js: - -- **Using nvm**: `nvm install 18 && nvm use 18` -- **Using n**: `n 18` -- **Download**: Visit [nodejs.org](https://nodejs.org/) - -#### Package Manager Not Found - -If you prefer a specific package manager but it's not installed: - -```bash -# Install pnpm -npm install -g pnpm - -# Install bun -curl -fsSL https://bun.sh/install | bash -``` - -#### Network Issues - -If you experience network timeouts or connection issues: - -```bash -# Try with different registry -npm config set registry https://registry.npmjs.org/ - -# Or clear npm cache -npm cache clean --force -``` - -## Next Steps - -Once installed, you're ready to create your first project: - -1. **Quick Start**: Follow our [Quick Start Guide](/docs/quick-start) -2. **Configuration**: Learn about [Configuration Options](/docs/frontend) -3. **CLI Reference**: Explore all [CLI Commands](/docs/cli-commands) - -## Staying Updated - -To ensure you're always using the latest version with bug fixes and new features: - -### Using npx (Automatic) -When using `npx create-better-t-stack@latest`, you automatically get the latest version. - -### Global Installation Updates - -If you have it installed globally, update regularly: - - - - ```bash - npm update -g create-better-t-stack - ``` - - - ```bash - pnpm update -g create-better-t-stack - ``` - - - ```bash - bun update -g create-better-t-stack - ``` - - - -### Release Notes - -Stay informed about new features and changes: - -- **GitHub Releases**: [View releases](https://github.com/AmanVarshney01/create-better-t-stack/releases) -- **Changelog**: Check the repository for detailed changes -- **Breaking Changes**: Major version updates may include breaking changes diff --git a/apps/web/content/docs/meta.json b/apps/web/content/docs/meta.json index 1b83440..0753258 100644 --- a/apps/web/content/docs/meta.json +++ b/apps/web/content/docs/meta.json @@ -1,32 +1,12 @@ { "pages": [ "index", - "installation", - "quick-start", + "cli", "project-structure", - "---Configuration Options---", - "frontend", - "backend", - "database", - "orm", - "authentication", - "runtime", - "api-layer", - "---Addons & Features---", - "addons", - "examples", - "deployment", - "---Database Setup---", - "database-providers", - "---CLI Reference---", - "cli-commands", - "cli-flags", - "---Guides---", - "migration-guide", - "troubleshooting", - "best-practices", - "---FAQ---", - "faq", - "compatibility" + "bts-config", + "analytics", + "contributing", + "compatibility", + "faq" ] } diff --git a/apps/web/content/docs/project-structure.mdx b/apps/web/content/docs/project-structure.mdx new file mode 100644 index 0000000..466dbc7 --- /dev/null +++ b/apps/web/content/docs/project-structure.mdx @@ -0,0 +1,60 @@ +--- +title: Project Structure +description: High-level overview of the generated monorepo layout +--- + +import { File, Folder, Files } from 'fumadocs-ui/components/files'; + +### Server-based projects + + + + + + + + + + + + + + + + +Notes: +- `apps/server` is present for backends like `hono`, `express`, `fastify`, `elysia`, `next`. +- `apps/web` and `apps/native` are optional; they appear only if you select a web or native frontend. + +### Convex-based projects + + + + + + + + + + + + + + + + + + +Notes: +- Convex replaces the server app; `packages/backend` is generated instead of `apps/server`. +- Auth, DB/ORM, and API scaffolding are disabled for Convex presets. + +### Where features land (high level) + +- API layer: merged into `apps/server` and `apps/web` when applicable. +- Database & ORM: merged into `apps/server` when both are selected. +- Authentication: merged into `apps/server`, `apps/web`, and `apps/native` when enabled and compatible. +- Addons: PWA merges into `apps/web`; others merge at the appropriate location. +- Web deployment (Workers): deployment files merge into `apps/web` per frontend. +- Runtime extras (Workers): Workers-specific files added at the repo root. + diff --git a/apps/web/src/app/(home)/_components/footer.tsx b/apps/web/src/app/(home)/_components/footer.tsx index bfa4651..fa6ee50 100644 --- a/apps/web/src/app/(home)/_components/footer.tsx +++ b/apps/web/src/app/(home)/_components/footer.tsx @@ -18,7 +18,7 @@ const Footer = () => {

{
  • GitHub Repository diff --git a/apps/web/src/app/(home)/_components/navbar.tsx b/apps/web/src/app/(home)/_components/navbar.tsx index a0780d0..2794f18 100644 --- a/apps/web/src/app/(home)/_components/navbar.tsx +++ b/apps/web/src/app/(home)/_components/navbar.tsx @@ -85,7 +85,7 @@ export default function Navbar() { target: "_blank", }, { - href: "https://www.github.com/better-t-stack/create-better-t-stack", + href: "https://www.github.com/AmanVarshney01/create-better-t-stack", label: "GitHub", icon: , target: "_blank", diff --git a/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx b/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx index 9abfa28..6d60549 100644 --- a/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx +++ b/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx @@ -48,7 +48,7 @@ export function AnalyticsHeader({ Source:{" "} - - ⚠️ WORK IN PROGRESS DONT TAKE REFERENCE!!! - - {children} - - ); + return {children}; } diff --git a/bun.lock b/bun.lock index 82ee483..d4278b0 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ }, "apps/cli": { "name": "create-better-t-stack", - "version": "2.29.2", + "version": "2.29.3", "bin": { "create-better-t-stack": "dist/index.js", },