From bd487b7e66502256d4ca21ea3d2bcc3db9b14755 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Sat, 29 Mar 2025 16:29:00 +0530 Subject: [PATCH] Add frontend selection options and improve CLI command generation --- .../app/(home)/_components/StackArchitech.tsx | 116 +++++++++++++++--- 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/apps/web/src/app/(home)/_components/StackArchitech.tsx b/apps/web/src/app/(home)/_components/StackArchitech.tsx index 078036e..fffc9d2 100644 --- a/apps/web/src/app/(home)/_components/StackArchitech.tsx +++ b/apps/web/src/app/(home)/_components/StackArchitech.tsx @@ -69,6 +69,32 @@ const triggerConfetti = () => { }; const TECH_OPTIONS = { + frontend: [ + { + id: "web", + name: "React Web", + description: "React with TanStack Router", + icon: "🌐", + color: "from-blue-400 to-blue-600", + default: true, + }, + { + id: "native", + name: "React Native", + description: "Expo with NativeWind", + icon: "📱", + color: "from-purple-400 to-purple-600", + default: false, + }, + { + id: "none", + name: "No Frontend", + description: "API-only backend", + icon: "⚙️", + color: "from-gray-400 to-gray-600", + default: false, + }, + ], runtime: [ { id: "bun", @@ -284,6 +310,7 @@ const TECH_OPTIONS = { }; interface StackState { + frontend: string[]; runtime: string; backendFramework: string; database: string; @@ -298,6 +325,7 @@ interface StackState { } const DEFAULT_STACK: StackState = { + frontend: ["web"], runtime: "bun", backendFramework: "hono", database: "sqlite", @@ -323,11 +351,21 @@ const StackArchitect = () => { }, [stack]); const generateCommand = useCallback((stackState: StackState) => { - const base = "npx create-better-t-stack"; - const projectName = "my-better-t-app"; - const flags: string[] = ["-y"]; + let base: string; + if (stackState.packageManager === "npm") { + base = "npx create-better-t-stack@latest"; + } else if (stackState.packageManager === "pnpm") { + base = "pnpm create better-t-stack@latest"; + } else { + base = "bun create better-t-stack@latest"; + } - const isDefault = + const projectName = "my-better-t-app"; + const flags: string[] = []; + + const isAllDefault = + stackState.frontend.length === 1 && + stackState.frontend[0] === "web" && stackState.runtime === "bun" && stackState.backendFramework === "hono" && stackState.database === "sqlite" && @@ -340,14 +378,26 @@ const StackArchitect = () => { stackState.git === "true" && stackState.install === "true"; - if (isDefault) return `${base} ${projectName} -y`; - - if (stackState.runtime === "node") { - flags.push("--runtime node"); + if (isAllDefault) { + return `${base} ${projectName} -y`; } - if (stackState.backendFramework === "elysia") { - flags.push("--elysia"); + flags.push("-y"); + + if (!stackState.frontend.includes("web")) { + flags.push("--no-web"); + } + + if (stackState.frontend.includes("native")) { + flags.push("--native"); + } + + if (stackState.runtime !== "bun") { + flags.push(`--runtime ${stackState.runtime}`); + } + + if (stackState.backendFramework !== "hono") { + flags.push(`--${stackState.backendFramework}`); } if (stackState.database === "postgres") { @@ -356,7 +406,7 @@ const StackArchitect = () => { flags.push("--no-database"); } - if (stackState.orm === "prisma") { + if (stackState.orm === "prisma" && stackState.database !== "none") { flags.push("--prisma"); } @@ -364,7 +414,7 @@ const StackArchitect = () => { flags.push("--no-auth"); } - if (stackState.turso === "true") { + if (stackState.turso === "true" && stackState.database === "sqlite") { flags.push("--turso"); } @@ -390,14 +440,35 @@ const StackArchitect = () => { flags.push("--no-install"); } - return flags.length > 0 - ? `${base} ${projectName} ${flags.join(" ")}` - : `${base} ${projectName}`; + return `${base} ${projectName} ${flags.join(" ")}`; }, []); const handleTechSelect = useCallback( (category: keyof typeof TECH_OPTIONS, techId: string) => { setStack((prev) => { + if (category === "frontend") { + const currentSelection = [...prev.frontend]; + + if (techId === "none") { + return { + ...prev, + frontend: [], + }; + } + + if (currentSelection.includes(techId)) { + return { + ...prev, + frontend: currentSelection.filter((id) => id !== techId), + }; + } + + return { + ...prev, + frontend: [...currentSelection, techId], + }; + } + if (category === "addons" || category === "examples") { const currentArray = [...(prev[category] || [])]; const index = currentArray.indexOf(techId); @@ -518,6 +589,8 @@ const StackArchitect = () => { let isSelected = false; if (activeTab === "addons" || activeTab === "examples") { isSelected = stack[activeTab].includes(tech.id); + } else if (activeTab === "frontend") { + isSelected = stack.frontend.includes(tech.id); } else { isSelected = stack[activeTab as keyof StackState] === tech.id; @@ -591,6 +664,19 @@ const StackArchitect = () => { Selected Stack
+ {stack.frontend.map((frontendId) => { + const frontend = TECH_OPTIONS.frontend.find( + (f) => f.id === frontendId, + ); + return frontend ? ( + + {frontend.icon} {frontend.name} + + ) : null; + })} { TECH_OPTIONS.runtime.find((t) => t.id === stack.runtime)