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

@@ -0,0 +1,208 @@
import alchemy from "alchemy";
{{#if (eq webDeploy "alchemy")}}
{{#if (includes frontend "next")}}
import { Next } from "alchemy/cloudflare";
{{else if (includes frontend "nuxt")}}
import { Nuxt } from "alchemy/cloudflare";
{{else if (includes frontend "svelte")}}
import { SvelteKit } from "alchemy/cloudflare";
{{else if (includes frontend "tanstack-start")}}
import { TanStackStart } from "alchemy/cloudflare";
{{else if (includes frontend "tanstack-router")}}
import { Vite } from "alchemy/cloudflare";
{{else if (includes frontend "react-router")}}
import { ReactRouter } from "alchemy/cloudflare";
{{else if (includes frontend "solid")}}
import { Vite } from "alchemy/cloudflare";
{{/if}}
{{/if}}
{{#if (eq serverDeploy "alchemy")}}
import { Worker, WranglerJson } from "alchemy/cloudflare";
{{#if (eq dbSetup "d1")}}
import { D1Database } from "alchemy/cloudflare";
{{/if}}
{{/if}}
{{#if (and (eq serverDeploy "alchemy") (eq dbSetup "d1"))}}
import { Exec } from "alchemy/os";
{{/if}}
import { config } from "dotenv";
{{#if (and (eq webDeploy "alchemy") (eq serverDeploy "alchemy"))}}
config({ path: "./.env" });
config({ path: "./apps/web/.env" });
config({ path: "./apps/server/.env" });
{{else if (or (eq webDeploy "alchemy") (eq serverDeploy "alchemy"))}}
config({ path: "./.env" });
{{/if}}
const app = await alchemy("{{projectName}}");
{{#if (and (eq serverDeploy "alchemy") (eq dbSetup "d1"))}}
await Exec("db-generate", {
{{#if (and (eq webDeploy "alchemy") (eq serverDeploy "alchemy"))}}cwd: "apps/server",{{/if}}
command: "{{packageManager}} run db:generate",
});
const db = await D1Database("database", {
name: `${app.name}-${app.stage}-db`,
migrationsDir: "apps/server/src/db/migrations",
});
{{/if}}
{{#if (eq webDeploy "alchemy")}}
{{#if (includes frontend "next")}}
export const web = await Next("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
bindings: {
{{#if (eq backend "convex")}}
NEXT_PUBLIC_CONVEX_URL: process.env.NEXT_PUBLIC_CONVEX_URL || "",
{{else}}
NEXT_PUBLIC_SERVER_URL: process.env.NEXT_PUBLIC_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "nuxt")}}
export const web = await Nuxt("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
bindings: {
{{#if (eq backend "convex")}}
NUXT_PUBLIC_CONVEX_URL: process.env.NUXT_PUBLIC_CONVEX_URL || "",
{{else}}
NUXT_PUBLIC_SERVER_URL: process.env.NUXT_PUBLIC_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "svelte")}}
export const web = await SvelteKit("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
bindings: {
{{#if (eq backend "convex")}}
PUBLIC_CONVEX_URL: process.env.PUBLIC_CONVEX_URL || "",
{{else}}
PUBLIC_SERVER_URL: process.env.PUBLIC_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "tanstack-start")}}
export const web = await TanStackStart("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
bindings: {
{{#if (eq backend "convex")}}
VITE_CONVEX_URL: process.env.VITE_CONVEX_URL || "",
{{else}}
VITE_SERVER_URL: process.env.VITE_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "tanstack-router")}}
export const web = await Vite("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
assets: "dist",
bindings: {
{{#if (eq backend "convex")}}
VITE_CONVEX_URL: process.env.VITE_CONVEX_URL || "",
{{else}}
VITE_SERVER_URL: process.env.VITE_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "react-router")}}
export const web = await ReactRouter("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
bindings: {
{{#if (eq backend "convex")}}
VITE_CONVEX_URL: process.env.VITE_CONVEX_URL || "",
{{else}}
VITE_SERVER_URL: process.env.VITE_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{else if (includes frontend "solid")}}
export const web = await Vite("web", {
{{#if (eq serverDeploy "alchemy")}}cwd: "apps/web",{{/if}}
name: `${app.name}-${app.stage}-web`,
assets: "dist",
bindings: {
{{#if (eq backend "convex")}}
VITE_CONVEX_URL: process.env.VITE_CONVEX_URL || "",
{{else}}
VITE_SERVER_URL: process.env.VITE_SERVER_URL || "",
{{/if}}
},
dev: {
command: "{{packageManager}} run dev"
}
});
{{/if}}
{{/if}}
{{#if (eq serverDeploy "alchemy")}}
export const server = await Worker("server", {
{{#if (eq webDeploy "alchemy")}}cwd: "apps/server",{{/if}}
name: `${app.name}-${app.stage}`,
entrypoint: "src/index.ts",
compatibility: "node",
bindings: {
{{#if (eq dbSetup "d1")}}
DB: db,
{{else if (and (ne database "none") (ne dbSetup "none"))}}
DATABASE_URL: alchemy.secret(process.env.DATABASE_URL),
{{/if}}
CORS_ORIGIN: process.env.CORS_ORIGIN || "",
{{#if auth}}
BETTER_AUTH_SECRET: alchemy.secret(process.env.BETTER_AUTH_SECRET),
BETTER_AUTH_URL: process.env.BETTER_AUTH_URL || "",
{{/if}}
{{#if (includes examples "ai")}}
GOOGLE_GENERATIVE_AI_API_KEY: alchemy.secret(process.env.GOOGLE_GENERATIVE_AI_API_KEY),
{{/if}}
{{#if (eq dbSetup "turso")}}
DATABASE_AUTH_TOKEN: alchemy.secret(process.env.DATABASE_AUTH_TOKEN),
{{/if}}
},
dev: {
port: 3000,
},
});
await WranglerJson("wrangler", {
worker: server,
});
{{/if}}
{{#if (and (eq webDeploy "alchemy") (eq serverDeploy "alchemy"))}}
console.log(`Web -> ${web.url}`);
console.log(`Server -> ${server.url}`);
{{else if (eq webDeploy "alchemy")}}
console.log(`Web -> ${web.url}`);
{{else if (eq serverDeploy "alchemy")}}
console.log(`Server -> ${server.url}`);
{{/if}}
await app.finalize();

View File

@@ -0,0 +1,20 @@
// This file infers types for the cloudflare:workers environment from your Alchemy Worker.
// @see https://alchemy.run/concepts/bindings/#type-safe-bindings
{{#if (eq webDeploy "alchemy")}}
import type { server } from "../../alchemy.run";
{{else}}
import type { server } from "./alchemy.run";
{{/if}}
export type CloudflareEnv = typeof server.Env;
declare global {
type Env = CloudflareEnv;
}
declare module "cloudflare:workers" {
namespace Cloudflare {
export interface Env extends CloudflareEnv {}
}
}

View File

@@ -0,0 +1,11 @@
// This is a temporary wrangler.jsonc file that will be overwritten by alchemy
// It's only here so that `wrangler dev` can work or use alchemy dev instead
{
"name": "{{projectName}}",
"main": "src/index.ts",
"compatibility_date": "2025-08-16",
"compatibility_flags": [
"nodejs_compat",
"nodejs_compat_populate_process_env"
]
}

View File

@@ -0,0 +1,34 @@
{
"name": "{{projectName}}-server",
"main": "src/index.ts",
"compatibility_date": "2025-06-15",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"NODE_ENV": "production"
// Add public environment variables here
// Example: "CORS_ORIGIN": "https://your-domain.com"
}
// For sensitive data, use:
// wrangler secret put SECRET_NAME
// Don't add secrets to "vars" - they're visible in the dashboard!
{{#if (eq dbSetup "d1")}},
// To set up D1 database:
// 1. Run: wrangler login
// 2. Run: wrangler d1 create your-database-name
// 3. Copy the output and paste below
// Then run migrations:
// bun db:generate
// To apply migrations locally, run:
// wrangler d1 migrations apply YOUR_DB_NAME --local
"d1_databases": [
{
"binding": "DB",
"database_name": "YOUR_DB_NAME",
"database_id": "YOUR_DB_ID",
"preview_database_id": "local-test-db",
"migrations_dir": "./src/db/migrations"
}
]
{{/if}}
}

View File

@@ -3,7 +3,7 @@
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"main": "./.output/server/index.mjs",
"compatibility_date": "2025-07-01",

View File

@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"main": ".open-next/worker.js",
"name": "{{projectName}}",
"compatibility_date": "2025-07-05",

View File

@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"compatibility_date": "2025-04-03",
"assets": {

View File

@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"compatibility_date": "2025-04-03",
"assets": {

View File

@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"main": ".output/server/index.mjs",
"compatibility_date": "2025-07-05",

View File

@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"compatibility_date": "2025-04-03",
"assets": {

View File

@@ -3,7 +3,7 @@
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "../../node_modules/wrangler/config-schema.json",
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "{{projectName}}",
"main": ".svelte-kit/cloudflare/_worker.js",
"compatibility_date": "2025-07-05",