mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
feat(web): improve docs and refactor cli (#476)
This commit is contained in:
36
README.md
36
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
|
||||
|
||||
|
||||
@@ -139,14 +139,3 @@ export const ADDON_COMPATIBILITY: Record<Addons, readonly Frontend[]> = {
|
||||
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",
|
||||
];
|
||||
|
||||
@@ -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<API>({
|
||||
message: "Select API type",
|
||||
|
||||
@@ -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<Examples>({
|
||||
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)) {
|
||||
|
||||
@@ -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<Frontend>({
|
||||
message: "Choose web",
|
||||
|
||||
@@ -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<string> {
|
||||
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<string> {
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -17,9 +17,7 @@ export type Backend = z.infer<typeof BackendSchema>;
|
||||
|
||||
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<typeof RuntimeSchema>;
|
||||
|
||||
export const FrontendSchema = z
|
||||
@@ -176,5 +174,3 @@ export interface BetterTStackConfig {
|
||||
api: API;
|
||||
webDeploy: WebDeploy;
|
||||
}
|
||||
|
||||
export type AvailablePackageManagers = "npm" | "pnpm" | "bun";
|
||||
|
||||
315
apps/cli/src/utils/compatibility-rules.ts
Normal file
315
apps/cli/src/utils/compatibility-rules.ts
Normal file
@@ -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<string>,
|
||||
options: CLIInput,
|
||||
config: Partial<ProjectConfig>,
|
||||
) {
|
||||
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<ProjectConfig>) {
|
||||
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<string>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
11
apps/cli/src/utils/compatibility.ts
Normal file
11
apps/cli/src/utils/compatibility.ts
Normal file
@@ -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;
|
||||
@@ -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}` : "";
|
||||
|
||||
@@ -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<ProjectConfig>) {
|
||||
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<string> {
|
||||
|
||||
55
apps/web/content/docs/analytics.mdx
Normal file
55
apps/web/content/docs/analytics.mdx
Normal file
@@ -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_<random>`
|
||||
- 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)
|
||||
|
||||
54
apps/web/content/docs/bts-config.mdx
Normal file
54
apps/web/content/docs/bts-config.mdx
Normal file
@@ -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)
|
||||
|
||||
@@ -3,6 +3,32 @@ title: CLI Commands
|
||||
description: Complete reference for all Better-T-Stack CLI commands and options
|
||||
---
|
||||
|
||||
## Quick Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
# Interactive setup
|
||||
bun create better-t-stack@latest
|
||||
|
||||
# With project name
|
||||
bun create better-t-stack@latest my-project
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest
|
||||
pnpm create better-t-stack@latest my-project
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest
|
||||
npx create-better-t-stack@latest my-project
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 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
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```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 .
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```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 .
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```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 .
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 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
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Flags
|
||||
|
||||
@@ -341,9 +414,23 @@ Display Better-T-Stack project sponsors.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
npx create-better-t-stack@latest sponsors
|
||||
```
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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
|
||||
```
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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
|
||||
```
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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.
|
||||
|
||||
35
apps/web/content/docs/cli/add.mdx
Normal file
35
apps/web/content/docs/cli/add.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: add
|
||||
description: Add addons or deployment to an existing project
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest add [options]
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest add [options]
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest add [options]
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 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)
|
||||
|
||||
25
apps/web/content/docs/cli/builder.mdx
Normal file
25
apps/web/content/docs/cli/builder.mdx
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: builder
|
||||
description: Open the web-based stack builder
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest builder
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
25
apps/web/content/docs/cli/docs.mdx
Normal file
25
apps/web/content/docs/cli/docs.mdx
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: docs
|
||||
description: Open the documentation
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest docs
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
23
apps/web/content/docs/cli/index.mdx
Normal file
23
apps/web/content/docs/cli/index.mdx
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: CLI
|
||||
description: Command reference index
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card href="/docs/cli/init" title="init">
|
||||
Create a new Better‑T‑Stack project (prompts or --yes)
|
||||
</Card>
|
||||
<Card href="/docs/cli/add" title="add">
|
||||
Add addons or deployment to an existing project
|
||||
</Card>
|
||||
<Card href="/docs/cli/builder" title="builder">
|
||||
Open the web-based Stack Builder
|
||||
</Card>
|
||||
<Card href="/docs/cli/docs" title="docs">
|
||||
Open documentation
|
||||
</Card>
|
||||
<Card href="/docs/cli/sponsors" title="sponsors">
|
||||
View project sponsors
|
||||
</Card>
|
||||
</Cards>
|
||||
|
||||
35
apps/web/content/docs/cli/init.mdx
Normal file
35
apps/web/content/docs/cli/init.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: init
|
||||
description: Create a new Better-T-Stack project
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest [project-directory]
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest [project-directory]
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest [project-directory]
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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)
|
||||
|
||||
5
apps/web/content/docs/cli/meta.json
Normal file
5
apps/web/content/docs/cli/meta.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"title": "CLI",
|
||||
"defaultOpen": true,
|
||||
"pages": ["index", "init", "add", "builder", "docs", "sponsors"]
|
||||
}
|
||||
25
apps/web/content/docs/cli/sponsors.mdx
Normal file
25
apps/web/content/docs/cli/sponsors.mdx
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: sponsors
|
||||
description: View project sponsors
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
<Tabs items={['bun', 'pnpm', 'npm']}>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun create better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm create better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npx create-better-t-stack@latest sponsors
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
29
apps/web/content/docs/compatibility.mdx
Normal file
29
apps/web/content/docs/compatibility.mdx
Normal file
@@ -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
|
||||
|
||||
94
apps/web/content/docs/contributing.mdx
Normal file
94
apps/web/content/docs/contributing.mdx
Normal file
@@ -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 <your-branch>
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
<Cards>
|
||||
<Card href="/docs/cli" title="CLI (per-command)">
|
||||
Flags, usage, and examples for each command
|
||||
</Card>
|
||||
<Card href="/docs/project-structure" title="Project Structure">
|
||||
See how web/server/native and Convex layouts are generated
|
||||
</Card>
|
||||
<Card href="/docs/compatibility" title="Compatibility">
|
||||
Valid combinations for backend, runtime, database, ORM, API
|
||||
</Card>
|
||||
<Card href="/docs/bts-config" title="bts.jsonc">
|
||||
Required for the add command; safe to delete if you don’t use add
|
||||
</Card>
|
||||
<Card href="/docs/contributing" title="Contributing">
|
||||
Dev setup and contribution flow
|
||||
</Card>
|
||||
</Cards>
|
||||
|
||||
@@ -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:
|
||||
|
||||
<Tabs items={['npm', 'pnpm', 'bun']}>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npm install -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm add -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun add -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
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:
|
||||
|
||||
<Tabs items={['npm', 'pnpm', 'bun']}>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npm update -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm update -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="bun">
|
||||
```bash
|
||||
bun update -g create-better-t-stack
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
60
apps/web/content/docs/project-structure.mdx
Normal file
60
apps/web/content/docs/project-structure.mdx
Normal file
@@ -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
|
||||
|
||||
<Files>
|
||||
<Folder name="apps" defaultOpen>
|
||||
<Folder name="server" defaultOpen>
|
||||
<Folder name="src" />
|
||||
</Folder>
|
||||
<Folder name="web" defaultOpen disabled>
|
||||
<Folder name="src" />
|
||||
<Folder name="public" />
|
||||
</Folder>
|
||||
<Folder name="native" defaultOpen disabled>
|
||||
<Folder name="app" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
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
|
||||
|
||||
<Files>
|
||||
<Folder name="apps" defaultOpen>
|
||||
<Folder name="web" defaultOpen disabled>
|
||||
<Folder name="src" />
|
||||
<Folder name="public" />
|
||||
</Folder>
|
||||
<Folder name="native" defaultOpen disabled>
|
||||
<Folder name="app" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
<Folder name="packages" defaultOpen>
|
||||
<Folder name="backend" defaultOpen>
|
||||
<Folder name="convex" />
|
||||
</Folder>
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
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.
|
||||
|
||||
@@ -18,7 +18,7 @@ const Footer = () => {
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<Link
|
||||
href="https://github.com/better-t-stack/create-better-t-stack"
|
||||
href="https://github.com/AmanVarshney01/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="inline-flex items-center justify-center rounded-md p-2 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||
aria-label="GitHub Repository"
|
||||
@@ -44,7 +44,7 @@ const Footer = () => {
|
||||
<li>
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://github.com/better-t-stack/create-better-t-stack"
|
||||
href="https://github.com/AmanVarshney01/create-better-t-stack"
|
||||
className="inline-block transition-colors hover:text-primary focus:text-primary focus:outline-none"
|
||||
>
|
||||
GitHub Repository
|
||||
|
||||
@@ -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: <Github className="size-4" />,
|
||||
target: "_blank",
|
||||
|
||||
@@ -48,7 +48,7 @@ export function AnalyticsHeader({
|
||||
<span className=" text-muted-foreground">
|
||||
Source:{" "}
|
||||
<Link
|
||||
href="https://github.com/amanvarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/analytics.ts"
|
||||
href="https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/analytics.ts"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-accent underline hover:text-primary"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Banner } from "fumadocs-ui/components/banner";
|
||||
import { DocsLayout, type DocsLayoutProps } from "fumadocs-ui/layouts/docs";
|
||||
import type { ReactNode } from "react";
|
||||
import { baseOptions } from "@/app/layout.config";
|
||||
@@ -15,12 +14,5 @@ const docsOptions: DocsLayoutProps = {
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<Banner variant="rainbow">
|
||||
⚠️ WORK IN PROGRESS DONT TAKE REFERENCE!!!
|
||||
</Banner>
|
||||
<DocsLayout {...docsOptions}>{children}</DocsLayout>
|
||||
</>
|
||||
);
|
||||
return <DocsLayout {...docsOptions}>{children}</DocsLayout>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user