From 91fe9f861fbf4dc2166b1161e4a6aa7142ee2023 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 26 Mar 2025 11:41:41 +0530 Subject: [PATCH] Add backend framework selection between hono, elysiajs --- .changeset/fruity-melons-argue.md | 5 + apps/cli/package.json | 5 +- apps/cli/src/constants.ts | 12 +- .../src/helpers/backend-framework-setup.ts | 41 +++++++ apps/cli/src/helpers/create-project.ts | 36 ++++-- apps/cli/src/helpers/post-installation.ts | 4 +- apps/cli/src/helpers/runtime-setup.ts | 98 ++++++++++----- apps/cli/src/helpers/template-manager.ts | 116 +++++++++++++++--- apps/cli/src/index.ts | 26 ++-- apps/cli/src/prompts/addons.ts | 2 + apps/cli/src/prompts/backend-framework.ts | 44 ++++--- apps/cli/src/prompts/config-prompts.ts | 2 +- apps/cli/src/prompts/database.ts | 3 +- apps/cli/src/prompts/orm.ts | 3 +- apps/cli/src/prompts/runtime.ts | 3 +- apps/cli/src/prompts/turso.ts | 3 +- apps/cli/src/types.ts | 10 +- apps/cli/src/utils/display-config.ts | 37 ++++-- .../utils/generate-reproducible-command.ts | 59 +++------ .../template/base/apps/server/package.json | 2 - .../template/base/apps/server/tsconfig.json | 2 +- .../server/src/lib/with-elysia-context.ts} | 8 +- .../apps/server/src/lib/with-hono-context.ts} | 6 +- .../src/with-drizzle-postgres-lib}/auth.ts | 2 +- .../src/with-drizzle-sqlite-lib}/auth.ts | 0 .../apps/server/src/with-elysia-index.ts | 38 ++++++ .../src/{index.ts => with-hono-index.ts} | 5 +- .../src/with-prisma-postgres-lib}/auth.ts | 0 .../src/with-prisma-sqlite-lib}/auth.ts | 0 .../apps/server/src/routers/todo.ts | 44 ------- .../apps/server/src/with-auth-lib/context.ts | 18 --- .../apps/server/src/with-auth-lib/trpc.ts | 24 ---- .../apps/server/src/with-auth-lib/trpc.ts | 24 ---- .../with-elysia/apps/server/src/index.ts | 27 ++++ .../apps/server/src/lib/context.ts | 13 ++ .../apps/server/src/index.ts | 4 +- .../apps/server/src/lib/context.ts | 4 +- .../apps/server/src/with-auth-lib/context.ts | 18 --- .../apps/server/src/with-auth-lib/trpc.ts | 24 ---- .../apps/server/src/with-auth-lib/trpc.ts | 24 ---- 40 files changed, 451 insertions(+), 345 deletions(-) create mode 100644 .changeset/fruity-melons-argue.md create mode 100644 apps/cli/src/helpers/backend-framework-setup.ts rename apps/cli/template/{with-prisma-sqlite/apps/server/src/with-auth-lib/context.ts => with-auth/apps/server/src/lib/with-elysia-context.ts} (53%) rename apps/cli/template/{with-drizzle-sqlite/apps/server/src/with-auth-lib/context.ts => with-auth/apps/server/src/lib/with-hono-context.ts} (67%) rename apps/cli/template/{with-drizzle-postgres/apps/server/src/with-auth-lib => with-auth/apps/server/src/with-drizzle-postgres-lib}/auth.ts (100%) rename apps/cli/template/{with-drizzle-sqlite/apps/server/src/with-auth-lib => with-auth/apps/server/src/with-drizzle-sqlite-lib}/auth.ts (100%) create mode 100644 apps/cli/template/with-auth/apps/server/src/with-elysia-index.ts rename apps/cli/template/with-auth/apps/server/src/{index.ts => with-hono-index.ts} (90%) rename apps/cli/template/{with-prisma-postgres/apps/server/src/with-auth-lib => with-auth/apps/server/src/with-prisma-postgres-lib}/auth.ts (100%) rename apps/cli/template/{with-prisma-sqlite/apps/server/src/with-auth-lib => with-auth/apps/server/src/with-prisma-sqlite-lib}/auth.ts (100%) delete mode 100644 apps/cli/template/with-drizzle-postgres/apps/server/src/routers/todo.ts delete mode 100644 apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/context.ts delete mode 100644 apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/trpc.ts delete mode 100644 apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/trpc.ts create mode 100644 apps/cli/template/with-elysia/apps/server/src/index.ts create mode 100644 apps/cli/template/with-elysia/apps/server/src/lib/context.ts rename apps/cli/template/{base => with-hono}/apps/server/src/index.ts (87%) rename apps/cli/template/{base => with-hono}/apps/server/src/lib/context.ts (67%) delete mode 100644 apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/context.ts delete mode 100644 apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/trpc.ts delete mode 100644 apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/trpc.ts diff --git a/.changeset/fruity-melons-argue.md b/.changeset/fruity-melons-argue.md new file mode 100644 index 0000000..f162054 --- /dev/null +++ b/.changeset/fruity-melons-argue.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": minor +--- + +Add option to choose elysiajs as backend framework diff --git a/apps/cli/package.json b/apps/cli/package.json index 958855a..a96bc39 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -7,10 +7,7 @@ "bin": { "create-better-t-stack": "dist/index.js" }, - "files": [ - "dist", - "template" - ], + "files": ["dist", "template"], "keywords": [], "repository": { "type": "git", diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 2e68dab..f7444b8 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -1,6 +1,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import type { ProjectConfig } from "./types"; +import { getUserPkgManager } from "./utils/get-package-manager"; const __filename = fileURLToPath(import.meta.url); const distPath = path.dirname(__filename); @@ -14,7 +15,7 @@ export const DEFAULT_CONFIG: ProjectConfig = { addons: [], examples: [], git: true, - packageManager: "npm", + packageManager: getUserPkgManager(), noInstall: false, turso: false, backendFramework: "hono", @@ -48,6 +49,15 @@ export const dependencyVersionMap = { "@types/node": "^22.13.11", "@types/bun": "^1.2.6", + + "@elysiajs/node": "^1.2.6", + + "@elysiajs/cors": "^1.2.0", + "@elysiajs/trpc": "^1.1.0", + elysia: "^1.2.25", + + "@hono/trpc-server": "^0.3.4", + hono: "^4.7.5", } as const; export type AvailableDependencies = keyof typeof dependencyVersionMap; diff --git a/apps/cli/src/helpers/backend-framework-setup.ts b/apps/cli/src/helpers/backend-framework-setup.ts new file mode 100644 index 0000000..f76c369 --- /dev/null +++ b/apps/cli/src/helpers/backend-framework-setup.ts @@ -0,0 +1,41 @@ +import path from "node:path"; +import type { AvailableDependencies } from "../constants"; +import type { BackendFramework, Runtime } from "../types"; +import { addPackageDependency } from "../utils/add-package-deps"; + +export async function setupBackendDependencies( + projectDir: string, + framework: BackendFramework, + runtime: Runtime, +): Promise { + const serverDir = path.join(projectDir, "apps/server"); + + const dependencies: AvailableDependencies[] = []; + const devDependencies: AvailableDependencies[] = []; + + if (framework === "hono") { + dependencies.push("hono", "@hono/trpc-server"); + + if (runtime === "node") { + dependencies.push("@hono/node-server"); + devDependencies.push("tsx", "@types/node"); + } + } else if (framework === "elysia") { + dependencies.push("elysia", "@elysiajs/cors", "@elysiajs/trpc"); + + if (runtime === "node") { + dependencies.push("@elysiajs/node"); + devDependencies.push("tsx", "@types/node"); + } + } + + if (runtime === "bun") { + devDependencies.push("@types/bun"); + } + + addPackageDependency({ + dependencies, + devDependencies, + projectDir: serverDir, + }); +} diff --git a/apps/cli/src/helpers/create-project.ts b/apps/cli/src/helpers/create-project.ts index 03dcd3f..abd846e 100644 --- a/apps/cli/src/helpers/create-project.ts +++ b/apps/cli/src/helpers/create-project.ts @@ -5,6 +5,7 @@ import pc from "picocolors"; import type { ProjectConfig } from "../types"; import { setupAddons } from "./addons-setup"; import { setupAuth } from "./auth-setup"; +import { setupBackendDependencies } from "./backend-framework-setup"; import { createReadme } from "./create-readme"; import { setupDatabase } from "./db-setup"; import { setupEnvironmentVariables } from "./env-setup"; @@ -16,6 +17,7 @@ import { copyBaseTemplate, fixGitignoreFiles, setupAuthTemplate, + setupBackendFramework, setupOrmTemplate, } from "./template-manager"; @@ -27,10 +29,14 @@ export async function createProject(options: ProjectConfig): Promise { await fs.ensureDir(projectDir); await copyBaseTemplate(projectDir); - await fixGitignoreFiles(projectDir); - await setupAuthTemplate(projectDir, options.auth); + await setupBackendFramework(projectDir, options.backendFramework); + await setupBackendDependencies( + projectDir, + options.backendFramework, + options.runtime, + ); await setupOrmTemplate( projectDir, @@ -39,15 +45,6 @@ export async function createProject(options: ProjectConfig): Promise { options.auth, ); - await setupRuntime(projectDir, options.runtime); - - await setupExamples( - projectDir, - options.examples, - options.orm, - options.auth, - ); - await setupDatabase( projectDir, options.database, @@ -55,8 +52,24 @@ export async function createProject(options: ProjectConfig): Promise { options.turso ?? options.database === "sqlite", ); + await setupAuthTemplate( + projectDir, + options.auth, + options.backendFramework, + options.orm, + options.database, + ); await setupAuth(projectDir, options.auth); + await setupRuntime(projectDir, options.runtime, options.backendFramework); + + await setupExamples( + projectDir, + options.examples, + options.orm, + options.auth, + ); + await setupEnvironmentVariables(projectDir, options); await initializeGit(projectDir, options.git); @@ -66,7 +79,6 @@ export async function createProject(options: ProjectConfig): Promise { } await updatePackageConfigurations(projectDir, options); - await createReadme(projectDir, options); displayPostInstallInstructions( diff --git a/apps/cli/src/helpers/post-installation.ts b/apps/cli/src/helpers/post-installation.ts index 187a8b0..82d6bad 100644 --- a/apps/cli/src/helpers/post-installation.ts +++ b/apps/cli/src/helpers/post-installation.ts @@ -33,9 +33,7 @@ export function displayPostInstallInstructions( ? getLintingInstructions(runCmd) : ""; - log.info(`${pc.cyan("Project created successfully!")} - -${pc.bold("Next steps:")} + log.info(`${pc.bold("Next steps:")} ${pc.cyan("1.")} ${cdCmd} ${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan(depsInstalled ? "2." : "3.")} ${runCmd} dev diff --git a/apps/cli/src/helpers/runtime-setup.ts b/apps/cli/src/helpers/runtime-setup.ts index 07fe948..bebf715 100644 --- a/apps/cli/src/helpers/runtime-setup.ts +++ b/apps/cli/src/helpers/runtime-setup.ts @@ -1,11 +1,12 @@ import path from "node:path"; import fs from "fs-extra"; -import type { Runtime } from "../types"; +import type { BackendFramework, Runtime } from "../types"; import { addPackageDependency } from "../utils/add-package-deps"; export async function setupRuntime( projectDir: string, runtime: Runtime, + backendFramework: BackendFramework, ): Promise { const serverDir = path.join(projectDir, "apps/server"); const serverIndexPath = path.join(serverDir, "src/index.ts"); @@ -13,9 +14,19 @@ export async function setupRuntime( const indexContent = await fs.readFile(serverIndexPath, "utf-8"); if (runtime === "bun") { - await setupBunRuntime(serverDir, serverIndexPath, indexContent); + await setupBunRuntime( + serverDir, + serverIndexPath, + indexContent, + backendFramework, + ); } else if (runtime === "node") { - await setupNodeRuntime(serverDir, serverIndexPath, indexContent); + await setupNodeRuntime( + serverDir, + serverIndexPath, + indexContent, + backendFramework, + ); } } @@ -23,6 +34,7 @@ async function setupBunRuntime( serverDir: string, serverIndexPath: string, indexContent: string, + backendFramework: BackendFramework, ): Promise { const packageJsonPath = path.join(serverDir, "package.json"); const packageJson = await fs.readJson(packageJsonPath); @@ -40,7 +52,7 @@ async function setupBunRuntime( projectDir: serverDir, }); - if (!indexContent.includes("export default app")) { + if (backendFramework === "hono") { const updatedContent = `${indexContent}\n\nexport default app;\n`; await fs.writeFile(serverIndexPath, updatedContent); } @@ -50,13 +62,8 @@ async function setupNodeRuntime( serverDir: string, serverIndexPath: string, indexContent: string, + backendFramework: BackendFramework, ): Promise { - addPackageDependency({ - dependencies: ["@hono/node-server"], - devDependencies: ["tsx", "@types/node"], - projectDir: serverDir, - }); - const packageJsonPath = path.join(serverDir, "package.json"); const packageJson = await fs.readJson(packageJsonPath); @@ -68,27 +75,62 @@ async function setupNodeRuntime( await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); - const importLine = 'import { serve } from "@hono/node-server";\n'; - const serverCode = ` + addPackageDependency({ + devDependencies: ["tsx", "@types/node"], + projectDir: serverDir, + }); + + if (backendFramework === "hono") { + addPackageDependency({ + dependencies: ["@hono/node-server"], + projectDir: serverDir, + }); + + const importLine = 'import { serve } from "@hono/node-server";\n'; + const serverCode = ` serve( - { - fetch: app.fetch, - port: 3000, - }, - (info) => { - console.log(\`Server is running on http://localhost:\${info.port}\`); - }, + { + fetch: app.fetch, + port: 3000, + }, + (info) => { + console.log(\`Server is running on http://localhost:\${info.port}\`); + }, );\n`; - if (!indexContent.includes("@hono/node-server")) { - const importEndIndex = indexContent.lastIndexOf("import"); - const importSection = indexContent.substring(0, importEndIndex); - const restOfFile = indexContent.substring(importEndIndex); + if (!indexContent.includes("@hono/node-server")) { + const importEndIndex = indexContent.lastIndexOf("import"); + const importSection = indexContent.substring(0, importEndIndex); + const restOfFile = indexContent.substring(importEndIndex); - const updatedContent = importSection + importLine + restOfFile + serverCode; - await fs.writeFile(serverIndexPath, updatedContent); - } else if (!indexContent.includes("serve(")) { - const updatedContent = indexContent + serverCode; - await fs.writeFile(serverIndexPath, updatedContent); + const updatedContent = + importSection + importLine + restOfFile + serverCode; + await fs.writeFile(serverIndexPath, updatedContent); + } + } else if (backendFramework === "elysia") { + addPackageDependency({ + dependencies: ["@elysiajs/node"], + projectDir: serverDir, + }); + + if (!indexContent.includes("@elysiajs/node")) { + const nodeImport = 'import { node } from "@elysiajs/node";\n'; + + const firstImportEnd = indexContent.indexOf( + "\n", + indexContent.indexOf("import"), + ); + const before = indexContent.substring(0, firstImportEnd + 1); + const after = indexContent.substring(firstImportEnd + 1); + + let updatedContent = before + nodeImport + after; + + updatedContent = updatedContent.replace( + /const app = new Elysia\([^)]*\)/, + "const app = new Elysia({ adapter: node() })", + ); + + await fs.writeFile(serverIndexPath, updatedContent); + } } } diff --git a/apps/cli/src/helpers/template-manager.ts b/apps/cli/src/helpers/template-manager.ts index 12ab85e..d4e7c96 100644 --- a/apps/cli/src/helpers/template-manager.ts +++ b/apps/cli/src/helpers/template-manager.ts @@ -1,7 +1,7 @@ import path from "node:path"; import fs from "fs-extra"; import { PKG_ROOT } from "../constants"; -import type { ProjectDatabase, ProjectOrm } from "../types"; +import type { BackendFramework, ProjectDatabase, ProjectOrm } from "../types"; export async function copyBaseTemplate(projectDir: string): Promise { const templateDir = path.join(PKG_ROOT, "template/base"); @@ -11,15 +11,13 @@ export async function copyBaseTemplate(projectDir: string): Promise { await fs.copy(templateDir, projectDir); } -export async function setupAuthTemplate( +export async function setupBackendFramework( projectDir: string, - auth: boolean, + framework: BackendFramework, ): Promise { - if (!auth) return; - - const authTemplateDir = path.join(PKG_ROOT, "template/with-auth"); - if (await fs.pathExists(authTemplateDir)) { - await fs.copy(authTemplateDir, projectDir, { overwrite: true }); + const frameworkDir = path.join(PKG_ROOT, `template/with-${framework}`); + if (await fs.pathExists(frameworkDir)) { + await fs.copy(frameworkDir, projectDir, { overwrite: true }); } } @@ -36,17 +34,83 @@ export async function setupOrmTemplate( if (await fs.pathExists(ormTemplateDir)) { await fs.copy(ormTemplateDir, projectDir, { overwrite: true }); - const serverSrcPath = path.join(projectDir, "apps/server/src"); - const libPath = path.join(serverSrcPath, "lib"); - const withAuthLibPath = path.join(serverSrcPath, "with-auth-lib"); - - if (auth) { - if (await fs.pathExists(withAuthLibPath)) { - await fs.remove(libPath); - await fs.move(withAuthLibPath, libPath); + if (!auth) { + if (orm === "prisma") { + const authSchemaPath = path.join( + projectDir, + "apps/server/prisma/schema/auth.prisma", + ); + if (await fs.pathExists(authSchemaPath)) { + await fs.remove(authSchemaPath); + } + } else if (orm === "drizzle") { + const authSchemaPath = path.join( + projectDir, + "apps/server/src/db/schema/auth.ts", + ); + if (await fs.pathExists(authSchemaPath)) { + await fs.remove(authSchemaPath); + } + } + } + } +} + +export async function setupAuthTemplate( + projectDir: string, + auth: boolean, + framework: BackendFramework, + orm: ProjectOrm, + database: ProjectDatabase, +): Promise { + if (!auth) return; + + const authTemplateDir = path.join(PKG_ROOT, "template/with-auth"); + if (await fs.pathExists(authTemplateDir)) { + const clientAuthDir = path.join(authTemplateDir, "apps/client"); + const projectClientDir = path.join(projectDir, "apps/client"); + await fs.copy(clientAuthDir, projectClientDir, { overwrite: true }); + + const serverAuthDir = path.join(authTemplateDir, "apps/server/src"); + const projectServerDir = path.join(projectDir, "apps/server/src"); + + await fs.copy( + path.join(serverAuthDir, "lib/trpc.ts"), + path.join(projectServerDir, "lib/trpc.ts"), + { overwrite: true }, + ); + + await fs.copy( + path.join(serverAuthDir, "routers/index.ts"), + path.join(projectServerDir, "routers/index.ts"), + { overwrite: true }, + ); + + const contextFileName = `with-${framework}-context.ts`; + await fs.copy( + path.join(serverAuthDir, "lib", contextFileName), + path.join(projectServerDir, "lib/context.ts"), + { overwrite: true }, + ); + + const indexFileName = `with-${framework}-index.ts`; + await fs.copy( + path.join(serverAuthDir, indexFileName), + path.join(projectServerDir, "index.ts"), + { overwrite: true }, + ); + + const authLibFileName = getAuthLibDir(orm, database); + const authLibSourceDir = path.join(serverAuthDir, authLibFileName); + if (await fs.pathExists(authLibSourceDir)) { + const files = await fs.readdir(authLibSourceDir); + for (const file of files) { + await fs.copy( + path.join(authLibSourceDir, file), + path.join(projectServerDir, "lib", file), + { overwrite: true }, + ); } - } else { - await fs.remove(withAuthLibPath); } } } @@ -81,3 +145,19 @@ function getOrmTemplateDir(orm: ProjectOrm, database: ProjectDatabase): string { return "template/base"; } + +function getAuthLibDir(orm: ProjectOrm, database: ProjectDatabase): string { + if (orm === "drizzle") { + return database === "sqlite" + ? "with-drizzle-sqlite-lib" + : "with-drizzle-postgres-lib"; + } + + if (orm === "prisma") { + return database === "sqlite" + ? "with-prisma-sqlite-lib" + : "with-prisma-postgres-lib"; + } + + throw new Error("Invalid ORM or database configuration for auth setup"); +} diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index a546095..e02dd72 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -6,6 +6,7 @@ import { createProject } from "./helpers/create-project"; import { installDependencies } from "./helpers/install-dependencies"; import { gatherConfig } from "./prompts/config-prompts"; import type { + BackendFramework, ProjectAddons, ProjectConfig, ProjectExamples, @@ -56,6 +57,7 @@ async function main() { .option("--turso", "Set up Turso for SQLite database") .option("--no-turso", "Skip Turso setup for SQLite database") .option("--hono", "Use Hono backend framework") + .option("--elysia", "Use Elysia backend framework") .option("--runtime ", "Specify runtime (bun or node)") .parse(); @@ -68,6 +70,10 @@ async function main() { const options = program.opts(); const projectDirectory = program.args[0]; + let backendFramework: BackendFramework | undefined; + if (options.hono) backendFramework = "hono"; + if (options.elysia) backendFramework = "elysia"; + const flagConfig: Partial = { ...(projectDirectory && { projectName: projectDirectory }), ...(options.database === false && { database: "none" }), @@ -82,7 +88,7 @@ async function main() { ...("git" in options && { git: options.git }), ...("install" in options && { noInstall: !options.install }), ...("turso" in options && { turso: options.turso }), - ...(options.hono && { backendFramework: "hono" }), + ...(backendFramework && { backendFramework }), ...(options.runtime && { runtime: options.runtime as Runtime }), ...((options.pwa || options.tauri || @@ -124,7 +130,11 @@ async function main() { database: options.database === false ? "none" - : (options.database ?? DEFAULT_CONFIG.database), + : options.sqlite + ? "sqlite" + : options.postgres + ? "postgres" + : DEFAULT_CONFIG.database, orm: options.database === false ? "none" @@ -133,12 +143,10 @@ async function main() { : options.prisma ? "prisma" : DEFAULT_CONFIG.orm, - auth: options.auth ?? DEFAULT_CONFIG.auth, - git: options.git ?? DEFAULT_CONFIG.git, + auth: "auth" in options ? options.auth : DEFAULT_CONFIG.auth, + git: "git" in options ? options.git : DEFAULT_CONFIG.git, noInstall: - "noInstall" in options - ? options.noInstall - : DEFAULT_CONFIG.noInstall, + "install" in options ? !options.install : DEFAULT_CONFIG.noInstall, packageManager: flagConfig.packageManager ?? DEFAULT_CONFIG.packageManager, addons: flagConfig.addons?.length @@ -153,9 +161,7 @@ async function main() { : flagConfig.database === "sqlite" ? DEFAULT_CONFIG.turso : false, - backendFramework: options.hono - ? "hono" - : DEFAULT_CONFIG.backendFramework, + backendFramework: backendFramework ?? DEFAULT_CONFIG.backendFramework, runtime: options.runtime ? (options.runtime as Runtime) : DEFAULT_CONFIG.runtime, diff --git a/apps/cli/src/prompts/addons.ts b/apps/cli/src/prompts/addons.ts index e45315b..17f9c37 100644 --- a/apps/cli/src/prompts/addons.ts +++ b/apps/cli/src/prompts/addons.ts @@ -1,5 +1,6 @@ import { cancel, isCancel, multiselect } from "@clack/prompts"; import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; import type { ProjectAddons } from "../types"; export async function getAddonsChoice( @@ -31,6 +32,7 @@ export async function getAddonsChoice( hint: "Add Git hooks with Husky, lint-staged (requires Biome)", }, ], + initialValues: DEFAULT_CONFIG.addons, required: false, }); diff --git a/apps/cli/src/prompts/backend-framework.ts b/apps/cli/src/prompts/backend-framework.ts index e57e246..acd1884 100644 --- a/apps/cli/src/prompts/backend-framework.ts +++ b/apps/cli/src/prompts/backend-framework.ts @@ -1,5 +1,6 @@ -// import { cancel, isCancel, select } from "@clack/prompts"; -// import pc from "picocolors"; +import { cancel, isCancel, select } from "@clack/prompts"; +import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; import type { BackendFramework } from "../types"; export async function getBackendFrameworkChoice( @@ -7,24 +8,27 @@ export async function getBackendFrameworkChoice( ): Promise { if (backendFramework !== undefined) return backendFramework; - return "hono"; + const response = await select({ + message: "Which backend framework would you like to use?", + options: [ + { + value: "hono", + label: "Hono", + hint: "Lightweight, ultrafast web framework", + }, + { + value: "elysia", + label: "Elysia", + hint: "TypeScript framework with end-to-end type safety)", + }, + ], + initialValue: DEFAULT_CONFIG.backendFramework, + }); - // const response = await select({ - // message: "Which backend framework would you like to use?", - // options: [ - // { - // value: "hono", - // label: "Hono", - // hint: "Lightweight, ultrafast web framework", - // }, - // ], - // initialValue: "hono", - // }); + if (isCancel(response)) { + cancel(pc.red("Operation cancelled")); + process.exit(0); + } - // if (isCancel(response)) { - // cancel(pc.red("Operation cancelled")); - // process.exit(0); - // } - - // return response; + return response; } diff --git a/apps/cli/src/prompts/config-prompts.ts b/apps/cli/src/prompts/config-prompts.ts index 140e5e0..6ff687f 100644 --- a/apps/cli/src/prompts/config-prompts.ts +++ b/apps/cli/src/prompts/config-prompts.ts @@ -46,8 +46,8 @@ export async function gatherConfig( projectName: async () => { return getProjectName(flags.projectName); }, - runtime: () => getRuntimeChoice(flags.runtime), backendFramework: () => getBackendFrameworkChoice(flags.backendFramework), + runtime: () => getRuntimeChoice(flags.runtime), database: () => getDatabaseChoice(flags.database), orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none"), diff --git a/apps/cli/src/prompts/database.ts b/apps/cli/src/prompts/database.ts index 8e5760d..d61c87a 100644 --- a/apps/cli/src/prompts/database.ts +++ b/apps/cli/src/prompts/database.ts @@ -1,5 +1,6 @@ import { cancel, isCancel, select } from "@clack/prompts"; import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; import type { ProjectDatabase } from "../types"; export async function getDatabaseChoice( @@ -26,7 +27,7 @@ export async function getDatabaseChoice( hint: "Traditional relational database", }, ], - initialValue: "sqlite", + initialValue: DEFAULT_CONFIG.database, }); if (isCancel(response)) { diff --git a/apps/cli/src/prompts/orm.ts b/apps/cli/src/prompts/orm.ts index 83211b5..2661202 100644 --- a/apps/cli/src/prompts/orm.ts +++ b/apps/cli/src/prompts/orm.ts @@ -1,5 +1,6 @@ import { cancel, isCancel, select } from "@clack/prompts"; import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; import type { ProjectOrm } from "../types"; export async function getORMChoice( @@ -23,7 +24,7 @@ export async function getORMChoice( hint: "Powerful, feature-rich ORM with schema migrations", }, ], - initialValue: "drizzle", + initialValue: DEFAULT_CONFIG.orm, }); if (isCancel(response)) { diff --git a/apps/cli/src/prompts/runtime.ts b/apps/cli/src/prompts/runtime.ts index 61d6d38..7d3281f 100644 --- a/apps/cli/src/prompts/runtime.ts +++ b/apps/cli/src/prompts/runtime.ts @@ -1,5 +1,6 @@ import { cancel, isCancel, select } from "@clack/prompts"; import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; import type { Runtime } from "../types"; export async function getRuntimeChoice(runtime?: Runtime): Promise { @@ -19,7 +20,7 @@ export async function getRuntimeChoice(runtime?: Runtime): Promise { hint: "Traditional Node.js runtime", }, ], - initialValue: "bun", + initialValue: DEFAULT_CONFIG.runtime, }); if (isCancel(response)) { diff --git a/apps/cli/src/prompts/turso.ts b/apps/cli/src/prompts/turso.ts index 4e52e12..c534e1c 100644 --- a/apps/cli/src/prompts/turso.ts +++ b/apps/cli/src/prompts/turso.ts @@ -1,12 +1,13 @@ import { cancel, confirm, isCancel } from "@clack/prompts"; import pc from "picocolors"; +import { DEFAULT_CONFIG } from "../constants"; export async function getTursoSetupChoice(turso?: boolean): Promise { if (turso !== undefined) return turso; const response = await confirm({ message: "Set up a Turso database for this project?", - initialValue: true, + initialValue: DEFAULT_CONFIG.turso, }); if (isCancel(response)) { diff --git a/apps/cli/src/types.ts b/apps/cli/src/types.ts index 76b5b61..23a2d37 100644 --- a/apps/cli/src/types.ts +++ b/apps/cli/src/types.ts @@ -1,13 +1,15 @@ export type ProjectDatabase = "sqlite" | "postgres" | "none"; export type ProjectOrm = "drizzle" | "prisma" | "none"; export type PackageManager = "npm" | "pnpm" | "bun"; -export type ProjectAddons = "pwa" | "tauri" | "biome" | "husky"; +export type ProjectAddons = "pwa" | "biome" | "tauri" | "husky"; +export type BackendFramework = "hono" | "elysia"; +export type Runtime = "node" | "bun"; export type ProjectExamples = "todo"; -export type BackendFramework = "hono"; -export type Runtime = "bun" | "node"; export interface ProjectConfig { projectName: string; + backendFramework: BackendFramework; + runtime: Runtime; database: ProjectDatabase; orm: ProjectOrm; auth: boolean; @@ -17,6 +19,4 @@ export interface ProjectConfig { packageManager: PackageManager; noInstall?: boolean; turso?: boolean; - backendFramework: BackendFramework; - runtime: Runtime; } diff --git a/apps/cli/src/utils/display-config.ts b/apps/cli/src/utils/display-config.ts index 632c465..1e502c9 100644 --- a/apps/cli/src/utils/display-config.ts +++ b/apps/cli/src/utils/display-config.ts @@ -7,32 +7,55 @@ export function displayConfig(config: Partial) { if (config.projectName) { configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`); } - if (config.database) { + + if (config.backendFramework !== undefined) { + configDisplay.push( + `${pc.blue("Backend Framework:")} ${config.backendFramework}`, + ); + } + + if (config.runtime !== undefined) { + configDisplay.push(`${pc.blue("Runtime:")} ${config.runtime}`); + } + + if (config.database !== undefined) { configDisplay.push(`${pc.blue("Database:")} ${config.database}`); } - if (config.orm) { + + if (config.orm !== undefined) { configDisplay.push(`${pc.blue("ORM:")} ${config.orm}`); } + if (config.auth !== undefined) { configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`); } - if (config.runtime) { - configDisplay.push(`${pc.blue("Runtime:")} ${config.runtime}`); + + if (config.addons !== undefined) { + const addonsText = + config.addons.length > 0 ? config.addons.join(", ") : "none"; + configDisplay.push(`${pc.blue("Addons:")} ${addonsText}`); } - if (config.addons?.length) { - configDisplay.push(`${pc.blue("Addons:")} ${config.addons.join(", ")}`); + + if (config.examples !== undefined) { + const examplesText = + config.examples.length > 0 ? config.examples.join(", ") : "none"; + configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`); } + if (config.git !== undefined) { configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`); } - if (config.packageManager) { + + if (config.packageManager !== undefined) { configDisplay.push( `${pc.blue("Package Manager:")} ${config.packageManager}`, ); } + if (config.noInstall !== undefined) { configDisplay.push(`${pc.blue("Skip Install:")} ${config.noInstall}`); } + if (config.turso !== undefined) { configDisplay.push(`${pc.blue("Turso Setup:")} ${config.turso}`); } diff --git a/apps/cli/src/utils/generate-reproducible-command.ts b/apps/cli/src/utils/generate-reproducible-command.ts index 4fb5698..5cb95c4 100644 --- a/apps/cli/src/utils/generate-reproducible-command.ts +++ b/apps/cli/src/utils/generate-reproducible-command.ts @@ -5,42 +5,36 @@ export function generateReproducibleCommand(config: ProjectConfig): string { if (config.database === "none") { flags.push("--no-database"); - } else if (config.database === "sqlite") { - flags.push("--sqlite"); - } else if (config.database === "postgres") { - flags.push("--postgres"); - } + } else { + flags.push(`--${config.database}`); - if (config.database !== "none") { - if (config.orm === "drizzle") { - flags.push("--drizzle"); - } else if (config.orm === "prisma") { - flags.push("--prisma"); + if (config.orm) { + flags.push(`--${config.orm}`); + } + + if (config.database === "sqlite") { + flags.push(config.turso ? "--turso" : "--no-turso"); } } - if (config.auth) { - flags.push("--auth"); - } else { - flags.push("--no-auth"); - } + flags.push(config.auth ? "--auth" : "--no-auth"); - if (config.git) { - flags.push("--git"); - } else { - flags.push("--no-git"); - } + flags.push(config.git ? "--git" : "--no-git"); - if (config.noInstall) { - flags.push("--no-install"); - } else { - flags.push("--install"); - } + flags.push(config.noInstall ? "--no-install" : "--install"); if (config.packageManager) { flags.push(`--${config.packageManager}`); } + if (config.backendFramework) { + flags.push(`--${config.backendFramework}`); + } + + if (config.runtime) { + flags.push(`--runtime ${config.runtime}`); + } + if (config.addons.length > 0) { for (const addon of config.addons) { flags.push(`--${addon}`); @@ -55,21 +49,8 @@ export function generateReproducibleCommand(config: ProjectConfig): string { flags.push("--no-examples"); } - if (config.database === "sqlite") { - if (config.turso) { - flags.push("--turso"); - } else { - flags.push("--no-turso"); - } - } - - if (config.runtime) { - flags.push(`--runtime ${config.runtime}`); - } - const baseCommand = "npx create-better-t-stack"; const projectName = config.projectName ? ` ${config.projectName}` : ""; - const flagString = flags.length > 0 ? ` ${flags.join(" ")}` : ""; - return `${baseCommand}${projectName}${flagString}`; + return `${baseCommand}${projectName} ${flags.join(" ")}`; } diff --git a/apps/cli/template/base/apps/server/package.json b/apps/cli/template/base/apps/server/package.json index 4f5ae89..8a79df8 100644 --- a/apps/cli/template/base/apps/server/package.json +++ b/apps/cli/template/base/apps/server/package.json @@ -8,10 +8,8 @@ "compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server" }, "dependencies": { - "@hono/trpc-server": "^0.3.4", "@trpc/server": "^11.0.0", "dotenv": "^16.4.7", - "hono": "^4.7.5", "zod": "^3.24.2" }, "devDependencies": { diff --git a/apps/cli/template/base/apps/server/tsconfig.json b/apps/cli/template/base/apps/server/tsconfig.json index d82751f..c09fccd 100644 --- a/apps/cli/template/base/apps/server/tsconfig.json +++ b/apps/cli/template/base/apps/server/tsconfig.json @@ -8,7 +8,7 @@ "skipLibCheck": true, "baseUrl": "./", "outDir": "./dist", - "types": ["node"], + "types": ["node", "bun"], "jsx": "react-jsx", "jsxImportSource": "hono/jsx" }, diff --git a/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/context.ts b/apps/cli/template/with-auth/apps/server/src/lib/with-elysia-context.ts similarity index 53% rename from apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/context.ts rename to apps/cli/template/with-auth/apps/server/src/lib/with-elysia-context.ts index c6eb0d8..b97faf5 100644 --- a/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/context.ts +++ b/apps/cli/template/with-auth/apps/server/src/lib/with-elysia-context.ts @@ -1,13 +1,13 @@ -import type { Context as HonoContext } from "hono"; +import type { Context as ElysiaContext } from "elysia"; import { auth } from "./auth"; export type CreateContextOptions = { - hono: HonoContext; + context: ElysiaContext; }; -export async function createContext({ hono }: CreateContextOptions) { +export async function createContext({ context }: CreateContextOptions) { const session = await auth.api.getSession({ - headers: hono.req.raw.headers, + headers: context.request.headers, }); return { diff --git a/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/context.ts b/apps/cli/template/with-auth/apps/server/src/lib/with-hono-context.ts similarity index 67% rename from apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/context.ts rename to apps/cli/template/with-auth/apps/server/src/lib/with-hono-context.ts index c6eb0d8..6eeee75 100644 --- a/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/context.ts +++ b/apps/cli/template/with-auth/apps/server/src/lib/with-hono-context.ts @@ -2,12 +2,12 @@ import type { Context as HonoContext } from "hono"; import { auth } from "./auth"; export type CreateContextOptions = { - hono: HonoContext; + context: HonoContext; }; -export async function createContext({ hono }: CreateContextOptions) { +export async function createContext({ context }: CreateContextOptions) { const session = await auth.api.getSession({ - headers: hono.req.raw.headers, + headers: context.req.raw.headers, }); return { diff --git a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/auth.ts b/apps/cli/template/with-auth/apps/server/src/with-drizzle-postgres-lib/auth.ts similarity index 100% rename from apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/auth.ts rename to apps/cli/template/with-auth/apps/server/src/with-drizzle-postgres-lib/auth.ts index dc2f935..ed45465 100644 --- a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/auth.ts +++ b/apps/cli/template/with-auth/apps/server/src/with-drizzle-postgres-lib/auth.ts @@ -1,7 +1,7 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import * as schema from "../db/schema/auth"; import { db } from "../db"; +import * as schema from "../db/schema/auth"; export const auth = betterAuth({ database: drizzleAdapter(db, { diff --git a/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/auth.ts b/apps/cli/template/with-auth/apps/server/src/with-drizzle-sqlite-lib/auth.ts similarity index 100% rename from apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/auth.ts rename to apps/cli/template/with-auth/apps/server/src/with-drizzle-sqlite-lib/auth.ts diff --git a/apps/cli/template/with-auth/apps/server/src/with-elysia-index.ts b/apps/cli/template/with-auth/apps/server/src/with-elysia-index.ts new file mode 100644 index 0000000..e1d0f13 --- /dev/null +++ b/apps/cli/template/with-auth/apps/server/src/with-elysia-index.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; +import { Elysia } from "elysia"; +import { cors } from "@elysiajs/cors"; +import { auth } from "./lib/auth"; +import { createContext } from "./lib/context"; +import { appRouter } from "./routers/index"; +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; + +const app = new Elysia() + .use( + cors({ + origin: process.env.CORS_ORIGIN || "", + methods: ["GET", "POST", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, + }), + ) + .all("/api/auth/*", async (context) => { + const { request } = context; + if (["POST", "GET"].includes(request.method)) { + return auth.handler(request); + } else { + context.error(405); + } + }) + .all("/trpc/*", async (context) => { + const res = await fetchRequestHandler({ + endpoint: "/trpc", + router: appRouter, + req: context.request, + createContext: () => createContext({ context }), + }); + return res; + }) + .get("/", () => "OK") + .listen(3000, () => { + console.log(`Server is running on http://localhost:3000`); + }); diff --git a/apps/cli/template/with-auth/apps/server/src/index.ts b/apps/cli/template/with-auth/apps/server/src/with-hono-index.ts similarity index 90% rename from apps/cli/template/with-auth/apps/server/src/index.ts rename to apps/cli/template/with-auth/apps/server/src/with-hono-index.ts index a92e6cd..b941a35 100644 --- a/apps/cli/template/with-auth/apps/server/src/index.ts +++ b/apps/cli/template/with-auth/apps/server/src/with-hono-index.ts @@ -1,3 +1,4 @@ + import { trpcServer } from "@hono/trpc-server"; import "dotenv/config"; import { Hono } from "hono"; @@ -27,8 +28,8 @@ app.use( "/trpc/*", trpcServer({ router: appRouter, - createContext: (_opts, hono) => { - return createContext({ hono }); + createContext: (_opts, context) => { + return createContext({ context }); }, }), ); diff --git a/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/auth.ts b/apps/cli/template/with-auth/apps/server/src/with-prisma-postgres-lib/auth.ts similarity index 100% rename from apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/auth.ts rename to apps/cli/template/with-auth/apps/server/src/with-prisma-postgres-lib/auth.ts diff --git a/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/auth.ts b/apps/cli/template/with-auth/apps/server/src/with-prisma-sqlite-lib/auth.ts similarity index 100% rename from apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/auth.ts rename to apps/cli/template/with-auth/apps/server/src/with-prisma-sqlite-lib/auth.ts diff --git a/apps/cli/template/with-drizzle-postgres/apps/server/src/routers/todo.ts b/apps/cli/template/with-drizzle-postgres/apps/server/src/routers/todo.ts deleted file mode 100644 index e2b1896..0000000 --- a/apps/cli/template/with-drizzle-postgres/apps/server/src/routers/todo.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { z } from "zod"; -import { router, publicProcedure } from "../lib/trpc"; -import { todo } from "../db/schema"; -import { eq } from "drizzle-orm"; -import { db } from "../db"; - -export const todoRouter = router({ - getAll: publicProcedure.query(async () => { - return await db.select().from(todo).all(); - }), - - create: publicProcedure - .input(z.object({ text: z.string().min(1) })) - .mutation(async ({ input }) => { - return await db - .insert(todo) - .values({ - text: input.text, - }) - .returning() - .get(); - }), - - toggle: publicProcedure - .input(z.object({ id: z.number(), completed: z.boolean() })) - .mutation(async ({ input }) => { - return await db - .update(todo) - .set({ completed: input.completed }) - .where(eq(todo.id, input.id)) - .returning() - .get(); - }), - - delete: publicProcedure - .input(z.object({ id: z.number() })) - .mutation(async ({ input }) => { - return await db - .delete(todo) - .where(eq(todo.id, input.id)) - .returning() - .get(); - }), -}); diff --git a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/context.ts b/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/context.ts deleted file mode 100644 index c6eb0d8..0000000 --- a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/context.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Context as HonoContext } from "hono"; -import { auth } from "./auth"; - -export type CreateContextOptions = { - hono: HonoContext; -}; - -export async function createContext({ hono }: CreateContextOptions) { - const session = await auth.api.getSession({ - headers: hono.req.raw.headers, - }); - - return { - session, - }; -} - -export type Context = Awaited>; diff --git a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/trpc.ts b/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/trpc.ts deleted file mode 100644 index 3affce2..0000000 --- a/apps/cli/template/with-drizzle-postgres/apps/server/src/with-auth-lib/trpc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import type { Context } from "./context"; - -export const t = initTRPC.context().create(); - -export const router = t.router; - -export const publicProcedure = t.procedure; - -export const protectedProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Authentication required", - cause: "No session", - }); - } - return next({ - ctx: { - ...ctx, - session: ctx.session, - }, - }); -}); diff --git a/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/trpc.ts b/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/trpc.ts deleted file mode 100644 index 3affce2..0000000 --- a/apps/cli/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/trpc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import type { Context } from "./context"; - -export const t = initTRPC.context().create(); - -export const router = t.router; - -export const publicProcedure = t.procedure; - -export const protectedProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Authentication required", - cause: "No session", - }); - } - return next({ - ctx: { - ...ctx, - session: ctx.session, - }, - }); -}); diff --git a/apps/cli/template/with-elysia/apps/server/src/index.ts b/apps/cli/template/with-elysia/apps/server/src/index.ts new file mode 100644 index 0000000..a58e3fe --- /dev/null +++ b/apps/cli/template/with-elysia/apps/server/src/index.ts @@ -0,0 +1,27 @@ +import "dotenv/config"; +import { Elysia } from "elysia"; +import { cors } from "@elysiajs/cors"; +import { createContext } from "./lib/context"; +import { appRouter } from "./routers/index"; +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; + +const app = new Elysia() + .use( + cors({ + origin: process.env.CORS_ORIGIN || "", + methods: ["GET", "POST", "OPTIONS"], + }), + ) + .all("/trpc/*", async (context) => { + const res = await fetchRequestHandler({ + endpoint: "/trpc", + router: appRouter, + req: context.request, + createContext: () => createContext({ context }), + }); + return res; + }) + .get("/", () => "OK") + .listen(3000, () => { + console.log(`Server is running on http://localhost:3000`); + }); diff --git a/apps/cli/template/with-elysia/apps/server/src/lib/context.ts b/apps/cli/template/with-elysia/apps/server/src/lib/context.ts new file mode 100644 index 0000000..6aaff80 --- /dev/null +++ b/apps/cli/template/with-elysia/apps/server/src/lib/context.ts @@ -0,0 +1,13 @@ +import type { Context as ElysiaContext } from "elysia"; + +export type CreateContextOptions = { + context: ElysiaContext; +}; + +export async function createContext({ context }: CreateContextOptions) { + return { + session: null, + }; +} + +export type Context = Awaited>; diff --git a/apps/cli/template/base/apps/server/src/index.ts b/apps/cli/template/with-hono/apps/server/src/index.ts similarity index 87% rename from apps/cli/template/base/apps/server/src/index.ts rename to apps/cli/template/with-hono/apps/server/src/index.ts index 116b3ce..b5a2566 100644 --- a/apps/cli/template/base/apps/server/src/index.ts +++ b/apps/cli/template/with-hono/apps/server/src/index.ts @@ -22,8 +22,8 @@ app.use( "/trpc/*", trpcServer({ router: appRouter, - createContext: (_opts, hono) => { - return createContext({ hono }); + createContext: (_opts, context) => { + return createContext({ context }); }, }), ); diff --git a/apps/cli/template/base/apps/server/src/lib/context.ts b/apps/cli/template/with-hono/apps/server/src/lib/context.ts similarity index 67% rename from apps/cli/template/base/apps/server/src/lib/context.ts rename to apps/cli/template/with-hono/apps/server/src/lib/context.ts index 3cad65f..bbcb4b2 100644 --- a/apps/cli/template/base/apps/server/src/lib/context.ts +++ b/apps/cli/template/with-hono/apps/server/src/lib/context.ts @@ -1,10 +1,10 @@ import type { Context as HonoContext } from "hono"; export type CreateContextOptions = { - hono: HonoContext; + context: HonoContext; }; -export async function createContext({ hono }: CreateContextOptions) { +export async function createContext({ context }: CreateContextOptions) { return { session: null, }; diff --git a/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/context.ts b/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/context.ts deleted file mode 100644 index c6eb0d8..0000000 --- a/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/context.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Context as HonoContext } from "hono"; -import { auth } from "./auth"; - -export type CreateContextOptions = { - hono: HonoContext; -}; - -export async function createContext({ hono }: CreateContextOptions) { - const session = await auth.api.getSession({ - headers: hono.req.raw.headers, - }); - - return { - session, - }; -} - -export type Context = Awaited>; diff --git a/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/trpc.ts b/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/trpc.ts deleted file mode 100644 index 3affce2..0000000 --- a/apps/cli/template/with-prisma-postgres/apps/server/src/with-auth-lib/trpc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import type { Context } from "./context"; - -export const t = initTRPC.context().create(); - -export const router = t.router; - -export const publicProcedure = t.procedure; - -export const protectedProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Authentication required", - cause: "No session", - }); - } - return next({ - ctx: { - ...ctx, - session: ctx.session, - }, - }); -}); diff --git a/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/trpc.ts b/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/trpc.ts deleted file mode 100644 index 3affce2..0000000 --- a/apps/cli/template/with-prisma-sqlite/apps/server/src/with-auth-lib/trpc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import type { Context } from "./context"; - -export const t = initTRPC.context().create(); - -export const router = t.router; - -export const publicProcedure = t.procedure; - -export const protectedProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Authentication required", - cause: "No session", - }); - } - return next({ - ctx: { - ...ctx, - session: ctx.session, - }, - }); -});