add express, automated mongodb atlas setup, fix stack architech

This commit is contained in:
Aman Varshney
2025-04-07 21:32:22 +05:30
parent c6c73fce76
commit 2cf01d155b
38 changed files with 902 additions and 393 deletions

View File

@@ -74,32 +74,45 @@ const StackArchitect = () => {
}, []);
useEffect(() => {
if (stack.database === "none" && stack.orm !== "none") {
setStack((prev) => ({ ...prev, orm: "none" }));
}
if (stack.database !== "postgres" || stack.orm !== "prisma") {
if (stack.prismaPostgres === "true") {
setStack((prev) => ({ ...prev, prismaPostgres: "false" }));
if (stack.database === "none") {
if (stack.orm !== "none") {
setStack((prev) => ({ ...prev, orm: "none" }));
}
if (stack.auth === "true") {
setStack((prev) => ({ ...prev, auth: "false" }));
}
if (stack.dbSetup !== "none") {
setStack((prev) => ({ ...prev, dbSetup: "none" }));
}
}
if (stack.database !== "sqlite" || stack.orm === "prisma") {
if (stack.turso === "true") {
setStack((prev) => ({ ...prev, turso: "false" }));
}
if (stack.database === "mongodb" && stack.orm === "drizzle") {
setStack((prev) => ({ ...prev, orm: "prisma" }));
}
if (stack.database === "none" && stack.auth === "true") {
setStack((prev) => ({ ...prev, auth: "false" }));
if (stack.dbSetup === "turso") {
if (stack.database !== "sqlite") {
setStack((prev) => ({ ...prev, database: "sqlite" }));
}
if (stack.orm === "prisma") {
setStack((prev) => ({ ...prev, orm: "drizzle" }));
}
} else if (stack.dbSetup === "prisma-postgres") {
if (stack.database !== "postgres") {
setStack((prev) => ({ ...prev, database: "postgres" }));
}
if (stack.orm !== "prisma") {
setStack((prev) => ({ ...prev, orm: "prisma" }));
}
} else if (stack.dbSetup === "mongodb-atlas") {
if (stack.database !== "mongodb") {
setStack((prev) => ({ ...prev, database: "mongodb" }));
}
if (stack.orm !== "prisma") {
setStack((prev) => ({ ...prev, orm: "prisma" }));
}
}
}, [
stack.database,
stack.orm,
stack.prismaPostgres,
stack.turso,
stack.auth,
]);
}, [stack.database, stack.orm, stack.dbSetup, stack.auth]);
useEffect(() => {
const cmd = generateCommand(stack);
@@ -113,6 +126,33 @@ const StackArchitect = () => {
notes.frontend = [];
notes.dbSetup = [];
if (stack.database === "none") {
notes.dbSetup.push("Database setup requires a database.");
} else {
if (stack.dbSetup === "turso") {
if (stack.database !== "sqlite") {
notes.dbSetup.push("Turso setup requires SQLite database.");
}
if (stack.orm === "prisma") {
notes.dbSetup.push("Turso is not compatible with Prisma ORM.");
}
} else if (stack.dbSetup === "prisma-postgres") {
if (stack.database !== "postgres") {
notes.dbSetup.push(
"Prisma PostgreSQL setup requires PostgreSQL database.",
);
}
if (stack.orm !== "prisma") {
notes.dbSetup.push("Prisma PostgreSQL setup requires Prisma ORM.");
}
} else if (stack.dbSetup === "mongodb-atlas") {
if (stack.database !== "mongodb") {
notes.dbSetup.push("MongoDB Atlas setup requires MongoDB database.");
}
}
}
notes.addons = [];
if (!hasWebFrontend) {
notes.addons.push("PWA and Tauri are only available with React Web.");
@@ -125,6 +165,8 @@ const StackArchitect = () => {
notes.orm.push(
"ORM options are only available when a database is selected.",
);
} else if (stack.database === "mongodb" && stack.orm === "drizzle") {
notes.orm.push("MongoDB is only available with Prisma ORM.");
}
notes.auth = [];
@@ -132,23 +174,6 @@ const StackArchitect = () => {
notes.auth.push("Authentication requires a database.");
}
notes.turso = [];
if (stack.database !== "sqlite") {
notes.turso.push(
"Turso integration is only available with SQLite database.",
);
}
if (stack.orm === "prisma") {
notes.turso.push("Turso is not compatible with Prisma ORM.");
}
notes.prismaPostgres = [];
if (stack.database !== "postgres" || stack.orm !== "prisma") {
notes.prismaPostgres.push(
"Prisma PostgreSQL setup requires PostgreSQL database with Prisma ORM.",
);
}
notes.examples = [];
if (!hasWebFrontend) {
notes.examples.push(
@@ -198,12 +223,8 @@ const StackArchitect = () => {
flags.push("--no-auth");
}
if (stackState.turso === "true") {
flags.push("--turso");
}
if (stackState.prismaPostgres === "true") {
flags.push("--prisma-postgres");
if (stackState.dbSetup !== "none") {
flags.push(`--db-setup ${stackState.dbSetup}`);
}
if (stackState.backendFramework !== "hono") {
@@ -263,7 +284,6 @@ const StackArchitect = () => {
if (currentSelection.length === 1) {
return prev;
}
return {
...prev,
frontend: currentSelection.filter((id) => id !== techId),
@@ -296,6 +316,10 @@ const StackArchitect = () => {
prev.frontend.includes("react-router") ||
prev.frontend.includes("tanstack-start");
const hasPWACompatibleFrontend =
prev.frontend.includes("tanstack-router") ||
prev.frontend.includes("react-router");
if (index >= 0) {
currentArray.splice(index, 1);
} else {
@@ -318,8 +342,7 @@ const StackArchitect = () => {
if (
category === "addons" &&
(techId === "pwa" || techId === "tauri") &&
!prev.frontend.includes("tanstack-router") &&
!prev.frontend.includes("react-router")
!hasPWACompatibleFrontend
) {
return prev;
}
@@ -342,45 +365,40 @@ const StackArchitect = () => {
}
if (category === "database") {
let updatedState = { ...prev, database: techId };
if (techId === "none") {
return {
...prev,
database: techId,
updatedState = {
...updatedState,
orm: "none",
turso: "false",
prismaPostgres: "false",
dbSetup: "none",
auth: "false",
};
}
} else if (prev.database === "none") {
updatedState.orm = techId === "mongodb" ? "prisma" : "drizzle";
updatedState.dbSetup = "none";
if (prev.database === "none") {
return {
...prev,
database: techId,
orm: "drizzle",
turso: techId === "sqlite" ? prev.turso : "false",
prismaPostgres:
techId === "postgres" && prev.orm === "prisma"
? prev.prismaPostgres
: "false",
auth:
hasWebFrontend(prev.frontend) ||
prev.frontend.includes("native")
? "true"
: "false",
};
}
const updatedState = {
...prev,
database: techId,
};
if (techId === "sqlite") {
updatedState.prismaPostgres = "false";
} else if (techId === "postgres" && prev.orm === "prisma") {
const hasCompatibleFrontend =
prev.frontend.length > 0 && !prev.frontend.includes("none");
if (hasCompatibleFrontend) {
updatedState.auth = "true";
}
} else {
updatedState.turso = "false";
if (techId === "mongodb" && updatedState.orm === "drizzle") {
updatedState.orm = "prisma";
}
if (updatedState.dbSetup !== "none") {
if (
(updatedState.dbSetup === "turso" && techId !== "sqlite") ||
(updatedState.dbSetup === "prisma-postgres" &&
techId !== "postgres") ||
(updatedState.dbSetup === "mongodb-atlas" &&
techId !== "mongodb")
) {
updatedState.dbSetup = "none";
}
}
}
return updatedState;
@@ -396,31 +414,41 @@ const StackArchitect = () => {
orm: techId,
};
if (techId === "prisma") {
updatedState.turso = "false";
if (prev.database === "postgres") {
} else {
updatedState.prismaPostgres = "false";
if (updatedState.dbSetup !== "none") {
if (
(updatedState.dbSetup === "turso" && techId === "prisma") ||
(updatedState.dbSetup === "prisma-postgres" &&
techId !== "prisma")
) {
updatedState.dbSetup = "none";
}
} else if (techId === "drizzle" || techId === "none") {
updatedState.prismaPostgres = "false";
}
return updatedState;
}
if (
category === "turso" &&
(prev.database !== "sqlite" || prev.orm === "prisma")
) {
return prev;
}
if (category === "dbSetup") {
if (prev.database === "none" && techId !== "none") {
return prev;
}
if (
category === "prismaPostgres" &&
(prev.database !== "postgres" || prev.orm !== "prisma")
) {
return prev;
const updatedState = {
...prev,
dbSetup: techId,
};
if (techId === "turso") {
updatedState.database = "sqlite";
updatedState.orm = "drizzle";
} else if (techId === "prisma-postgres") {
updatedState.database = "postgres";
updatedState.orm = "prisma";
} else if (techId === "mongodb-atlas") {
updatedState.database = "mongodb";
updatedState.orm = "prisma";
}
return updatedState;
}
if (
@@ -440,15 +468,6 @@ const StackArchitect = () => {
[],
);
const hasWebFrontend = useCallback((frontendOptions: string[]) => {
return (
frontendOptions.includes("tanstack-router") ||
frontendOptions.includes("react-router") ||
frontendOptions.includes("tanstack-start") ||
frontendOptions.includes("native")
);
}, []);
const copyToClipboard = useCallback(() => {
navigator.clipboard.writeText(command);
setCopied(true);
@@ -698,12 +717,16 @@ const StackArchitect = () => {
const isDisabled =
(activeTab === "orm" && stack.database === "none") ||
(activeTab === "turso" &&
(stack.database !== "sqlite" ||
stack.orm === "prisma")) ||
(activeTab === "prismaPostgres" &&
(stack.database !== "postgres" ||
stack.orm !== "prisma")) ||
(activeTab === "dbSetup" &&
((tech.id !== "none" && stack.database === "none") ||
(tech.id === "turso" &&
(stack.database !== "sqlite" ||
stack.orm === "prisma")) ||
(tech.id === "prisma-postgres" &&
(stack.database !== "postgres" ||
stack.orm !== "prisma")) ||
(tech.id === "mongodb-atlas" &&
stack.database !== "mongodb"))) ||
(activeTab === "examples" &&
(((tech.id === "todo" || tech.id === "ai") &&
!hasWebFrontendSelected) ||
@@ -866,7 +889,7 @@ const StackArchitect = () => {
}
</span>
{stack.orm && stack.database !== "none" && (
{stack.orm !== "none" && stack.database !== "none" && (
<span className="inline-flex items-center rounded border border-cyan-300 bg-cyan-100 px-1.5 py-0.5 text-cyan-800 text-xs dark:border-cyan-700/30 dark:bg-cyan-900/30 dark:text-cyan-300">
{TECH_OPTIONS.orm.find((t) => t.id === stack.orm)?.icon}{" "}
{TECH_OPTIONS.orm.find((t) => t.id === stack.orm)?.name}
@@ -882,37 +905,18 @@ const StackArchitect = () => {
</span>
)}
{stack.turso === "true" &&
stack.database === "sqlite" &&
stack.orm !== "prisma" && (
<span className="inline-flex items-center rounded border border-pink-300 bg-pink-100 px-1.5 py-0.5 text-pink-800 text-xs dark:border-pink-700/30 dark:bg-pink-900/30 dark:text-pink-300">
{
TECH_OPTIONS.turso.find((t) => t.id === stack.turso)
?.icon
}{" "}
{
TECH_OPTIONS.turso.find((t) => t.id === stack.turso)
?.name
}
</span>
)}
{stack.prismaPostgres === "true" &&
stack.database === "postgres" &&
stack.orm === "prisma" && (
<span className="inline-flex items-center rounded border border-indigo-300 bg-indigo-100 px-1.5 py-0.5 text-indigo-800 text-xs dark:border-indigo-700/30 dark:bg-indigo-900/30 dark:text-indigo-300">
{
TECH_OPTIONS.prismaPostgres.find(
(t) => t.id === stack.prismaPostgres,
)?.icon
}{" "}
{
TECH_OPTIONS.prismaPostgres.find(
(t) => t.id === stack.prismaPostgres,
)?.name
}
</span>
)}
{stack.dbSetup !== "none" && (
<span className="inline-flex items-center rounded border border-pink-300 bg-pink-100 px-1.5 py-0.5 text-pink-800 text-xs dark:border-pink-700/30 dark:bg-pink-900/30 dark:text-pink-300">
{
TECH_OPTIONS.dbSetup.find((t) => t.id === stack.dbSetup)
?.icon
}{" "}
{
TECH_OPTIONS.dbSetup.find((t) => t.id === stack.dbSetup)
?.name
}
</span>
)}
{stack.addons.map((addonId) => {
const addon = TECH_OPTIONS.addons.find(

View File

@@ -74,6 +74,13 @@ export const TECH_OPTIONS = {
icon: "🦊",
color: "from-purple-500 to-purple-700",
},
{
id: "express",
name: "Express",
description: "Popular Node.js framework",
icon: "🚂",
color: "from-gray-500 to-gray-700",
},
],
database: [
{
@@ -91,6 +98,13 @@ export const TECH_OPTIONS = {
icon: "🐘",
color: "from-indigo-400 to-indigo-600",
},
{
id: "mongodb",
name: "MongoDB",
description: "NoSQL document database",
icon: "🍃",
color: "from-green-400 to-green-600",
},
{
id: "none",
name: "No Database",
@@ -115,6 +129,44 @@ export const TECH_OPTIONS = {
icon: "◮",
color: "from-purple-400 to-purple-600",
},
{
id: "none",
name: "No ORM",
description: "Skip ORM integration",
icon: "🚫",
color: "from-gray-400 to-gray-600",
},
],
dbSetup: [
{
id: "turso",
name: "Turso",
description: "SQLite cloud database powered by libSQL",
icon: "☁️",
color: "from-pink-400 to-pink-600",
},
{
id: "prisma-postgres",
name: "Prisma PostgreSQL",
description: "Set up PostgreSQL with Prisma",
icon: "🐘",
color: "from-indigo-400 to-indigo-600",
},
{
id: "mongodb-atlas",
name: "MongoDB Atlas",
description: "Cloud MongoDB setup with Atlas",
icon: "🌩️",
color: "from-green-400 to-green-600",
},
{
id: "none",
name: "Basic Setup",
description: "No cloud DB integration",
icon: "💻",
color: "from-gray-400 to-gray-600",
default: true,
},
],
auth: [
{
@@ -133,42 +185,6 @@ export const TECH_OPTIONS = {
color: "from-red-400 to-red-600",
},
],
turso: [
{
id: "true",
name: "Turso",
description: "SQLite cloud database",
icon: "☁️",
color: "from-pink-400 to-pink-600",
default: false,
},
{
id: "false",
name: "No Turso",
description: "Skip Turso integration",
icon: "🚫",
color: "from-gray-400 to-gray-600",
default: true,
},
],
prismaPostgres: [
{
id: "true",
name: "Prisma PostgreSQL",
description: "Set up PostgreSQL with Prisma",
icon: "🐘",
color: "from-indigo-400 to-indigo-600",
default: false,
},
{
id: "false",
name: "Skip Prisma PostgreSQL",
description: "Basic Prisma setup",
icon: "🚫",
color: "from-gray-400 to-gray-600",
default: true,
},
],
packageManager: [
{
id: "npm",
@@ -293,9 +309,8 @@ export const PRESET_TEMPLATES = [
backendFramework: "hono",
database: "sqlite",
orm: "drizzle",
dbSetup: "none",
auth: "true",
turso: "false",
prismaPostgres: "false",
packageManager: "bun",
addons: [],
examples: [],
@@ -314,9 +329,8 @@ export const PRESET_TEMPLATES = [
backendFramework: "hono",
database: "sqlite",
orm: "drizzle",
dbSetup: "none",
auth: "true",
turso: "false",
prismaPostgres: "false",
packageManager: "bun",
addons: [],
examples: [],
@@ -335,9 +349,8 @@ export const PRESET_TEMPLATES = [
backendFramework: "hono",
database: "postgres",
orm: "drizzle",
dbSetup: "none",
auth: "false",
turso: "false",
prismaPostgres: "false",
packageManager: "bun",
addons: [],
examples: [],
@@ -356,9 +369,8 @@ export const PRESET_TEMPLATES = [
backendFramework: "hono",
database: "sqlite",
orm: "drizzle",
dbSetup: "turso",
auth: "true",
turso: "true",
prismaPostgres: "false",
packageManager: "bun",
addons: ["pwa", "biome", "husky", "tauri"],
examples: ["todo", "ai"],
@@ -374,10 +386,9 @@ export type StackState = {
runtime: string;
backendFramework: string;
database: string;
orm: string | null;
orm: string;
dbSetup: string;
auth: string;
turso: string;
prismaPostgres: string;
packageManager: string;
addons: string[];
examples: string[];
@@ -392,9 +403,8 @@ export const DEFAULT_STACK: StackState = {
backendFramework: "hono",
database: "sqlite",
orm: "drizzle",
dbSetup: "none",
auth: "true",
turso: "false",
prismaPostgres: "false",
packageManager: "bun",
addons: [],
examples: [],