feat(cli): add alchemy and improve cli tooling and structure (#520)

This commit is contained in:
Aman Varshney
2025-08-20 23:43:58 +05:30
committed by GitHub
parent c5430ae4fd
commit 5788876c47
152 changed files with 5804 additions and 2264 deletions

View File

@@ -11,6 +11,7 @@ import type {
PackageManager,
ProjectConfig,
Runtime,
ServerDeploy,
WebDeploy,
} from "../types";
import { exitCancelled } from "../utils/errors";
@@ -27,6 +28,7 @@ import { getinstallChoice } from "./install";
import { getORMChoice } from "./orm";
import { getPackageManagerChoice } from "./package-manager";
import { getRuntimeChoice } from "./runtime";
import { getServerDeploymentChoice } from "./server-deploy";
import { getDeploymentChoice } from "./web-deploy";
type PromptGroupResults = {
@@ -44,6 +46,7 @@ type PromptGroupResults = {
packageManager: PackageManager;
install: boolean;
webDeploy: WebDeploy;
serverDeploy: ServerDeploy;
};
export async function gatherConfig(
@@ -97,6 +100,13 @@ export async function gatherConfig(
results.backend,
results.frontend,
),
serverDeploy: ({ results }) =>
getServerDeploymentChoice(
flags.serverDeploy,
results.runtime,
results.backend,
results.webDeploy,
),
git: () => getGitChoice(flags.git),
packageManager: () => getPackageManagerChoice(flags.packageManager),
install: () => getinstallChoice(flags.install),
@@ -144,5 +154,6 @@ export async function gatherConfig(
dbSetup: result.dbSetup,
api: result.api,
webDeploy: result.webDeploy,
serverDeploy: result.serverDeploy,
};
}

View File

@@ -45,8 +45,8 @@ export async function getProjectName(initialName?: string): Promise<string> {
let counter = 1;
while (
fs.pathExistsSync(path.resolve(process.cwd(), defaultName)) &&
fs.readdirSync(path.resolve(process.cwd(), defaultName)).length > 0
(await fs.pathExists(path.resolve(process.cwd(), defaultName))) &&
(await fs.readdir(path.resolve(process.cwd(), defaultName))).length > 0
) {
defaultName = `${DEFAULT_CONFIG.projectName}-${counter}`;
counter++;

View File

@@ -0,0 +1,129 @@
import { isCancel, select } from "@clack/prompts";
import { DEFAULT_CONFIG } from "../constants";
import type { Backend, Runtime, ServerDeploy, WebDeploy } from "../types";
import { exitCancelled } from "../utils/errors";
type DeploymentOption = {
value: ServerDeploy;
label: string;
hint: string;
};
function getDeploymentDisplay(deployment: ServerDeploy): {
label: string;
hint: string;
} {
if (deployment === "wrangler") {
return {
label: "Wrangler",
hint: "Deploy to Cloudflare Workers using Wrangler",
};
}
if (deployment === "alchemy") {
return {
label: "Alchemy",
hint: "Deploy to Cloudflare Workers using Alchemy",
};
}
return {
label: deployment,
hint: `Add ${deployment} deployment`,
};
}
export async function getServerDeploymentChoice(
deployment?: ServerDeploy,
runtime?: Runtime,
backend?: Backend,
webDeploy?: WebDeploy,
): Promise<ServerDeploy> {
if (deployment !== undefined) return deployment;
if (backend === "none" || backend === "convex") {
return "none";
}
const options: DeploymentOption[] = [];
if (runtime === "workers") {
["alchemy", "wrangler"].forEach((deploy) => {
const { label, hint } = getDeploymentDisplay(deploy as ServerDeploy);
options.unshift({
value: deploy as ServerDeploy,
label,
hint,
});
});
} else {
options.push({ value: "none", label: "None", hint: "Manual setup" });
}
const response = await select<ServerDeploy>({
message: "Select server deployment",
options,
initialValue:
webDeploy === "alchemy"
? "alchemy"
: runtime === "workers"
? "wrangler"
: DEFAULT_CONFIG.serverDeploy,
});
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}
export async function getServerDeploymentToAdd(
runtime?: Runtime,
existingDeployment?: ServerDeploy,
): Promise<ServerDeploy> {
const options: DeploymentOption[] = [];
if (runtime === "workers") {
if (existingDeployment !== "wrangler") {
const { label, hint } = getDeploymentDisplay("wrangler");
options.push({
value: "wrangler",
label,
hint,
});
}
if (existingDeployment !== "alchemy") {
const { label, hint } = getDeploymentDisplay("alchemy");
options.push({
value: "alchemy",
label,
hint,
});
}
}
if (existingDeployment && existingDeployment !== "none") {
return "none";
}
if (options.length > 0) {
options.push({
value: "none",
label: "None",
hint: "Skip deployment setup",
});
}
if (options.length === 0) {
return "none";
}
const response = await select<ServerDeploy>({
message: "Select server deployment",
options,
initialValue:
runtime === "workers" ? "wrangler" : DEFAULT_CONFIG.serverDeploy,
});
if (isCancel(response)) return exitCancelled("Operation cancelled");
return response;
}

View File

@@ -18,12 +18,18 @@ function getDeploymentDisplay(deployment: WebDeploy): {
label: string;
hint: string;
} {
if (deployment === "workers") {
if (deployment === "wrangler") {
return {
label: "Cloudflare Workers",
label: "Wrangler",
hint: "Deploy to Cloudflare Workers using Wrangler",
};
}
if (deployment === "alchemy") {
return {
label: "Alchemy",
hint: "Deploy to Cloudflare Workers using Alchemy",
};
}
return {
label: deployment,
hint: `Add ${deployment} deployment`,
@@ -41,14 +47,16 @@ export async function getDeploymentChoice(
return "none";
}
const options: DeploymentOption[] = [
{
value: "workers",
label: "Cloudflare Workers",
hint: "Deploy to Cloudflare Workers using Wrangler",
const options: DeploymentOption[] = ["wrangler", "alchemy", "none"].map(
(deploy) => {
const { label, hint } = getDeploymentDisplay(deploy as WebDeploy);
return {
value: deploy as WebDeploy,
label,
hint,
};
},
{ value: "none", label: "None", hint: "Manual setup" },
];
);
const response = await select<WebDeploy>({
message: "Select web deployment",
@@ -71,10 +79,19 @@ export async function getDeploymentToAdd(
const options: DeploymentOption[] = [];
if (existingDeployment !== "workers") {
const { label, hint } = getDeploymentDisplay("workers");
if (existingDeployment !== "wrangler") {
const { label, hint } = getDeploymentDisplay("wrangler");
options.push({
value: "workers",
value: "wrangler",
label,
hint,
});
}
if (existingDeployment !== "alchemy") {
const { label, hint } = getDeploymentDisplay("alchemy");
options.push({
value: "alchemy",
label,
hint,
});