| 1 | import path from "node:path"; |
| 2 | import { execa } from "execa"; |
| 3 | |
| 4 | export interface AddPackageDependencyOptions { |
| 5 | dependencies: string[]; |
| 6 | devMode?: boolean; |
| 7 | projectName: string; |
| 8 | target?: "client" | "server"; |
| 9 | } |
| 10 | |
| 11 | export const addPackageDependency = async ( |
| 12 | opts: AddPackageDependencyOptions, |
| 13 | ) => { |
| 14 | const { dependencies, devMode = false, projectName, target } = opts; |
| 15 | |
| 16 | // Early validation - only validate project name, allow empty dependencies |
| 17 | if (!projectName.trim()) { |
| 18 | throw new Error("Project name is required"); |
| 19 | } |
| 20 | |
| 21 | // Construct base command args once |
| 22 | const baseArgs = ["install"]; |
| 23 | if (devMode) { |
| 24 | baseArgs.push("-D"); |
| 25 | } |
| 26 | const installArgs = [...baseArgs, ...dependencies]; |
| 27 | |
| 28 | // Determine working directory |
| 29 | const projectPath = path.resolve(process.cwd(), projectName); |
| 30 | let workingDir = projectPath; |
| 31 | |
| 32 | if (target) { |
| 33 | workingDir = path.join(projectPath, target); |
| 34 | } |
| 35 | |
| 36 | try { |
| 37 | await execa("bun", installArgs, { |
| 38 | cwd: workingDir, |
| 39 | }); |
| 40 | } catch (error) { |
| 41 | const targetSuffix = target ? ` in ${target}` : ""; |
| 42 | throw new Error( |
| 43 | `Failed to install dependencies${targetSuffix}: ${error instanceof Error ? error.message : String(error)}`, |
| 44 | ); |
| 45 | } |
| 46 | }; |