| 1 | import path from "node:path"; |
| 2 | import { consola } from "consola"; |
| 3 | import { execa } from "execa"; |
| 4 | import fs from "fs-extra"; |
| 5 | import pc from "picocolors"; |
| 6 | import yoctoSpinner from "yocto-spinner"; |
| 7 | import { honoClientTemplate, honoRpcTemplate } from "@/utils/templates"; |
| 8 | import type { ProjectOptions } from "@/types"; |
| 9 | import { EXTRAS_DIR } from "@/utils"; |
| 10 | import { nameGenerator } from "@/utils/name-generator"; |
| 11 | |
| 12 | export async function rpcInstaller( |
| 13 | options: Required<ProjectOptions>, |
| 14 | ): Promise<boolean> { |
| 15 | const spinner = yoctoSpinner({ text: "Setting up RPC client..." }).start(); |
| 16 | |
| 17 | try { |
| 18 | const { projectName, rpc, shadcn, tailwind } = options; |
| 19 | const projectPath = path.resolve(process.cwd(), projectName); |
| 20 | |
| 21 | // 1. Update client package.json to ensure hono client is installed |
| 22 | const clientPkgPath = path.join(projectPath, "client", "package.json"); |
| 23 | const clientPkg = await fs.readJson(clientPkgPath); |
| 24 | |
| 25 | if (!clientPkg.dependencies.hono) { |
| 26 | await execa("bun", ["install", "hono"], { cwd: projectPath }); |
| 27 | } |
| 28 | |
| 29 | await fs.writeJson(clientPkgPath, clientPkg, { spaces: 2 }); |
| 30 | |
| 31 | // 2. Update server package.json dev script for RPC |
| 32 | const serverPkgPath = path.join(projectPath, "server", "package.json"); |
| 33 | const serverPkg = await fs.readJson(serverPkgPath); |
| 34 | |
| 35 | // Update the dev script to include TypeScript compilation |
| 36 | serverPkg.scripts.dev = "bun --watch run src/index.ts && tsc --watch"; |
| 37 | |
| 38 | await fs.writeJson(serverPkgPath, serverPkg, { spaces: 2 }); |
| 39 | |
| 40 | // 3. Server modification for RPC export type (no client imports) |
| 41 | const serverIndexPath = path.join(projectPath, "server", "src", "index.ts"); |
| 42 | await fs.writeFile(serverIndexPath, honoRpcTemplate, "utf8"); |
| 43 | |
| 44 | // 4. Create separate client helper file |
| 45 | const clientHelperPath = path.join( |
| 46 | projectPath, |
| 47 | "server", |
| 48 | "src", |
| 49 | "client.ts", |
| 50 | ); |
| 51 | await fs.writeFile(clientHelperPath, honoClientTemplate, "utf8"); |
| 52 | |
| 53 | // 5. Update App.tsx based on template selection using switch statement |
| 54 | const appTsxSrc = path.join( |
| 55 | EXTRAS_DIR, |
| 56 | "client", |
| 57 | "src", |
| 58 | "App.tsx", |
| 59 | nameGenerator("App.tsx", { tailwind, shadcn, rpc }), |
| 60 | ); |
| 61 | const appTsxTarget = path.join(projectPath, "client", "src", "App.tsx"); |
| 62 | |
| 63 | fs.copySync(appTsxSrc, appTsxTarget); |
| 64 | spinner.success("RPC client setup completed"); |
| 65 | return true; |
| 66 | } catch (err: unknown) { |
| 67 | spinner.error("Failed to set up RPC client"); |
| 68 | if (err instanceof Error) { |
| 69 | consola.error(pc.red("Error:"), err.message); |
| 70 | } else { |
| 71 | consola.error(pc.red("Error: Unknown error")); |
| 72 | } |
| 73 | return false; |
| 74 | } |
| 75 | } |