mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
feat(cli): add alchemy and improve cli tooling and structure (#520)
This commit is contained in:
@@ -70,6 +70,7 @@ const CATEGORY_ORDER: Array<keyof typeof TECH_OPTIONS> = [
|
||||
"orm",
|
||||
"dbSetup",
|
||||
"webDeploy",
|
||||
"serverDeploy",
|
||||
"auth",
|
||||
"packageManager",
|
||||
"addons",
|
||||
@@ -122,6 +123,7 @@ const getBadgeColors = (category: string): string => {
|
||||
return "border-orange-300 bg-orange-100 text-orange-800 dark:border-orange-700/30 dark:bg-orange-900/30 dark:text-orange-300";
|
||||
case "git":
|
||||
case "webDeploy":
|
||||
case "serverDeploy":
|
||||
case "install":
|
||||
return "border-gray-300 bg-gray-100 text-gray-700 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400";
|
||||
default:
|
||||
@@ -716,10 +718,10 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
|
||||
if (!isPWACompat && nextStack.addons.includes("pwa")) {
|
||||
incompatibleAddons.push("pwa");
|
||||
notes.webFrontend.notes.push(
|
||||
"PWA addon requires TanStack/React Router or Solid. Addon will be removed.",
|
||||
"PWA addon requires TanStack Router, React Router, Solid, or Next.js. Addon will be removed.",
|
||||
);
|
||||
notes.addons.notes.push(
|
||||
"PWA requires TanStack/React Router/Solid. It will be removed.",
|
||||
"PWA requires TanStack Router, React Router, Solid, or Next.js. It will be removed.",
|
||||
);
|
||||
notes.webFrontend.hasIssue = true;
|
||||
notes.addons.hasIssue = true;
|
||||
@@ -731,10 +733,10 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
|
||||
if (!isTauriCompat && nextStack.addons.includes("tauri")) {
|
||||
incompatibleAddons.push("tauri");
|
||||
notes.webFrontend.notes.push(
|
||||
"Tauri addon requires TanStack/React Router, Nuxt, Svelte, Solid, or Next.js. Addon will be removed.",
|
||||
"Tauri addon requires TanStack Router, React Router, Nuxt, Svelte, Solid, or Next.js. Addon will be removed.",
|
||||
);
|
||||
notes.addons.notes.push(
|
||||
"Tauri requires TanStack/React Router/Nuxt/Svelte/Solid/Next.js. It will be removed.",
|
||||
"Tauri requires TanStack Router, React Router, Nuxt, Svelte, Solid, or Next.js. It will be removed.",
|
||||
);
|
||||
notes.webFrontend.hasIssue = true;
|
||||
notes.addons.hasIssue = true;
|
||||
@@ -881,6 +883,160 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
|
||||
});
|
||||
}
|
||||
|
||||
// Server deployment requires a backend (and not Convex)
|
||||
if (
|
||||
nextStack.serverDeploy !== "none" &&
|
||||
(nextStack.backend === "none" || nextStack.backend === "convex")
|
||||
) {
|
||||
notes.serverDeploy.notes.push(
|
||||
"Server deployment requires a supported backend. It will be disabled.",
|
||||
);
|
||||
notes.backend.notes.push(
|
||||
"No compatible backend selected: Server deployment has been disabled.",
|
||||
);
|
||||
notes.serverDeploy.hasIssue = true;
|
||||
notes.backend.hasIssue = true;
|
||||
nextStack.serverDeploy = "none";
|
||||
changed = true;
|
||||
changes.push({
|
||||
category: "serverDeploy",
|
||||
message: "Server deployment set to 'none' (requires backend)",
|
||||
});
|
||||
}
|
||||
|
||||
// Cloudflare server deployments (wrangler/alchemy) require Workers runtime
|
||||
if (nextStack.serverDeploy !== "none" && nextStack.runtime !== "workers") {
|
||||
notes.serverDeploy.notes.push(
|
||||
"Selected server deployment targets Cloudflare Workers. Runtime will be set to 'Cloudflare Workers'.",
|
||||
);
|
||||
notes.runtime.notes.push(
|
||||
"Server deployment requires Cloudflare Workers runtime. It will be selected.",
|
||||
);
|
||||
notes.serverDeploy.hasIssue = true;
|
||||
notes.runtime.hasIssue = true;
|
||||
nextStack.runtime = "workers";
|
||||
changed = true;
|
||||
changes.push({
|
||||
category: "serverDeploy",
|
||||
message:
|
||||
"Runtime set to 'Cloudflare Workers' (required by server deployment)",
|
||||
});
|
||||
|
||||
// Apply Workers runtime compatibility adjustments
|
||||
if (nextStack.backend !== "hono") {
|
||||
notes.runtime.notes.push(
|
||||
"Cloudflare Workers runtime requires Hono backend. Hono will be selected.",
|
||||
);
|
||||
notes.backend.notes.push(
|
||||
"Cloudflare Workers runtime requires Hono backend. It will be selected.",
|
||||
);
|
||||
notes.runtime.hasIssue = true;
|
||||
notes.backend.hasIssue = true;
|
||||
nextStack.backend = "hono";
|
||||
changes.push({
|
||||
category: "runtime",
|
||||
message: "Backend set to 'Hono' (required by Cloudflare Workers)",
|
||||
});
|
||||
}
|
||||
|
||||
if (nextStack.orm !== "drizzle" && nextStack.orm !== "none") {
|
||||
notes.runtime.notes.push(
|
||||
"Cloudflare Workers runtime requires Drizzle ORM or no ORM. Drizzle will be selected.",
|
||||
);
|
||||
notes.orm.notes.push(
|
||||
"Cloudflare Workers runtime requires Drizzle ORM or no ORM. Drizzle will be selected.",
|
||||
);
|
||||
notes.runtime.hasIssue = true;
|
||||
notes.orm.hasIssue = true;
|
||||
nextStack.orm = "drizzle";
|
||||
changes.push({
|
||||
category: "runtime",
|
||||
message: "ORM set to 'Drizzle' (required by Cloudflare Workers)",
|
||||
});
|
||||
}
|
||||
|
||||
if (nextStack.database === "mongodb") {
|
||||
notes.runtime.notes.push(
|
||||
"Cloudflare Workers runtime is not compatible with MongoDB. SQLite will be selected.",
|
||||
);
|
||||
notes.database.notes.push(
|
||||
"MongoDB is not compatible with Cloudflare Workers runtime. SQLite will be selected.",
|
||||
);
|
||||
notes.runtime.hasIssue = true;
|
||||
notes.database.hasIssue = true;
|
||||
nextStack.database = "sqlite";
|
||||
changes.push({
|
||||
category: "runtime",
|
||||
message:
|
||||
"Database set to 'SQLite' (MongoDB not compatible with Workers)",
|
||||
});
|
||||
}
|
||||
|
||||
if (nextStack.dbSetup === "docker") {
|
||||
notes.runtime.notes.push(
|
||||
"Cloudflare Workers runtime does not support Docker setup. D1 will be selected.",
|
||||
);
|
||||
notes.dbSetup.notes.push(
|
||||
"Docker setup is not compatible with Cloudflare Workers runtime. D1 will be selected.",
|
||||
);
|
||||
notes.runtime.hasIssue = true;
|
||||
notes.dbSetup.hasIssue = true;
|
||||
nextStack.dbSetup = "d1";
|
||||
changes.push({
|
||||
category: "runtime",
|
||||
message: "DB Setup set to 'D1' (Docker not compatible with Workers)",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Alchemy deployment validation - temporarily not compatible with Next.js and React Router
|
||||
const isAlchemyWebDeploy = nextStack.webDeploy === "alchemy";
|
||||
const isAlchemyServerDeploy = nextStack.serverDeploy === "alchemy";
|
||||
|
||||
if (isAlchemyWebDeploy || isAlchemyServerDeploy) {
|
||||
const incompatibleFrontends = nextStack.webFrontend.filter(
|
||||
(f) => f === "next" || f === "react-router",
|
||||
);
|
||||
|
||||
if (incompatibleFrontends.length > 0) {
|
||||
const deployType =
|
||||
isAlchemyWebDeploy && isAlchemyServerDeploy
|
||||
? "web and server deployment"
|
||||
: isAlchemyWebDeploy
|
||||
? "web deployment"
|
||||
: "server deployment";
|
||||
|
||||
notes.webFrontend.notes.push(
|
||||
`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")}. These frontends will be removed.`,
|
||||
);
|
||||
notes.webDeploy.notes.push(
|
||||
`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")}.`,
|
||||
);
|
||||
notes.serverDeploy.notes.push(
|
||||
`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")}.`,
|
||||
);
|
||||
notes.webFrontend.hasIssue = true;
|
||||
notes.webDeploy.hasIssue = true;
|
||||
notes.serverDeploy.hasIssue = true;
|
||||
|
||||
// Remove incompatible frontends
|
||||
nextStack.webFrontend = nextStack.webFrontend.filter(
|
||||
(f) => f !== "next" && f !== "react-router",
|
||||
);
|
||||
|
||||
// If no web frontends remain, set to default
|
||||
if (nextStack.webFrontend.length === 0) {
|
||||
nextStack.webFrontend = ["tanstack-router"];
|
||||
}
|
||||
|
||||
changed = true;
|
||||
changes.push({
|
||||
category: "alchemy",
|
||||
message: `Removed ${incompatibleFrontends.join(" and ")} (not compatible with Alchemy ${deployType})`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
adjustedStack: changed ? nextStack : null,
|
||||
notes,
|
||||
@@ -985,6 +1141,13 @@ const generateCommand = (stackState: StackState): string => {
|
||||
flags.push(`--web-deploy ${stackState.webDeploy}`);
|
||||
}
|
||||
|
||||
if (
|
||||
stackState.serverDeploy &&
|
||||
!checkDefault("serverDeploy", stackState.serverDeploy)
|
||||
) {
|
||||
flags.push(`--server-deploy ${stackState.serverDeploy}`);
|
||||
}
|
||||
|
||||
if (!checkDefault("install", stackState.install)) {
|
||||
if (stackState.install === "false" && DEFAULT_STACK.install === "true") {
|
||||
flags.push("--no-install");
|
||||
@@ -1461,6 +1624,19 @@ const StackBuilder = () => {
|
||||
const { adjustedStack } = analyzeStackCompatibility(simulatedStack);
|
||||
const finalStack = adjustedStack ?? simulatedStack;
|
||||
|
||||
// Additional check for Alchemy compatibility with Next.js and React Router
|
||||
if (
|
||||
category === "webFrontend" &&
|
||||
(optionId === "next" || optionId === "react-router")
|
||||
) {
|
||||
const isAlchemyWebDeploy = finalStack.webDeploy === "alchemy";
|
||||
const isAlchemyServerDeploy = finalStack.serverDeploy === "alchemy";
|
||||
|
||||
if (isAlchemyWebDeploy || isAlchemyServerDeploy) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
category === "webFrontend" ||
|
||||
category === "nativeFrontend" ||
|
||||
|
||||
Reference in New Issue
Block a user