import { execSync } from "child_process"; import fs from "fs-extra"; import path from "path"; import chalk from "chalk"; export type UILibrary = "Hiro UI" | "Material UI" | "Chakra UI" | "shadcn/ui" | "No UI Library"; export interface Answers { projectName: string; framework: "Next.js" | "React"; useTypeScript: boolean; uiLibrary: UILibrary; template: string; packageManager?: "npm" | "yarn" | "pnpm"; includeBackend?: boolean; } export async function createProject(answers: Answers) { const { projectName, framework, useTypeScript, uiLibrary, template, packageManager, includeBackend } = answers; const projectDir = path.join(process.cwd(), projectName); const pm = packageManager || "npm"; // 1️⃣ ایجاد پوشه پروژه console.log(chalk.cyan(`\n📁 Creating project folder at ${projectDir}...`)); fs.ensureDirSync(projectDir); process.chdir(projectDir); // 2️⃣ Frontend console.log(chalk.green(`\n🚀 Setting up ${framework} frontend...`)); try { if (framework === "Next.js") { const tsFlag = useTypeScript ? "--typescript" : ""; execSync(`npx create-next-app@latest . ${tsFlag} --eslint --import-alias @/*`, { stdio: "inherit" }); } else { const tsFlag = useTypeScript ? "--template typescript" : ""; execSync(`npx create-react-app . ${tsFlag}`, { stdio: "inherit" }); } } catch (err) { console.error(chalk.red("❌ Failed to create frontend."), err); return; } // 3️⃣ Tailwind + UI Library console.log(chalk.magenta("\n🎨 Installing TailwindCSS and UI library...")); try { execSync(`${pm} install -D tailwindcss postcss autoprefixer`, { stdio: "inherit" }); execSync("npx tailwindcss init -p", { stdio: "inherit" }); if (uiLibrary && uiLibrary !== "No UI Library") { switch (uiLibrary) { case "Hiro UI": execSync("npx hiro-ui init", { stdio: "inherit" }); break; case "shadcn/ui": execSync("npx shadcn-ui init", { stdio: "inherit" }); break; case "Material UI": execSync(`${pm} install @mui/material @emotion/react @emotion/styled`, { stdio: "inherit" }); break; case "Chakra UI": execSync(`${pm} install @chakra-ui/react @emotion/react @emotion/styled framer-motion`, { stdio: "inherit" }); break; } } console.log(chalk.green("✅ TailwindCSS and UI library installed.")); } catch (err) { console.warn(chalk.yellow("⚠️ Tailwind or UI library installation failed."), err); } // 4️⃣ Backend با TypeScript if (includeBackend) { console.log(chalk.green("\n🚀 Setting up Node.js backend with TypeScript...")); const backendDir = path.join(projectDir, "backend"); fs.ensureDirSync(backendDir); process.chdir(backendDir); execSync(`${pm} init -y`, { stdio: "inherit" }); execSync(`${pm} install express cors dotenv`, { stdio: "inherit" }); execSync(`${pm} install -D typescript ts-node @types/node @types/express nodemon`, { stdio: "inherit" }); fs.writeFileSync( path.join(backendDir, "tsconfig.json"), JSON.stringify( { compilerOptions: { target: "ES2020", module: "commonjs", strict: true, esModuleInterop: true, outDir: "dist", }, include: ["src"], }, null, 2 ) ); fs.ensureDirSync(path.join(backendDir, "src")); fs.writeFileSync( path.join(backendDir, "src", "index.ts"), ` import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json()); app.get('/api', (req, res) => { res.json({ message: "Backend is running!" }); }); const PORT = process.env.PORT || 4000; app.listen(PORT, () => console.log(\`Server running on http://localhost:\${PORT}\`)); ` ); // package.json scripts const pkgJsonPath = path.join(backendDir, "package.json"); const pkgJson = fs.readJSONSync(pkgJsonPath); pkgJson.scripts = { dev: "nodemon --watch src --exec ts-node src/index.ts", build: "tsc", }; fs.writeJSONSync(pkgJsonPath, pkgJson, { spaces: 2 }); process.chdir(projectDir); console.log(chalk.green("✅ Backend setup complete.")); } // 5️⃣ Docker Compose console.log(chalk.cyan("\n🐳 Generating docker-compose.yml...")); const dockerCompose = ` version: "3.8" services: frontend: build: . ports: - "3000:3000" command: ${framework === "Next.js" ? '"npm run dev"' : '"npm start"'} volumes: - .:/app working_dir: /app backend: build: ./backend ports: - "4000:4000" command: "npm run dev" volumes: - ./backend:/app working_dir: /app `; fs.writeFileSync(path.join(projectDir, "docker-compose.yml"), dockerCompose); console.log(chalk.green("✅ docker-compose.yml created. Run `docker-compose up` to start both frontend and backend.")); // 6️⃣ Template if (template && template !== "Blank") { console.log(chalk.blue(`\n📦 Applying "${template}" template...`)); const templateSource = path.resolve(__dirname, `../templates/${template.toLowerCase()}`); if (fs.existsSync(templateSource)) { fs.copySync(templateSource, projectDir, { overwrite: true }); console.log(chalk.green(`✅ Template "${template}" applied.`)); } else { console.warn(chalk.yellow(`⚠️ Template "${template}" not found.`)); } } console.log(chalk.bold.green(`\n🎉 Project "${projectName}" created successfully!`)); console.log(chalk.cyan("👉 Next steps:")); console.log(` ${chalk.yellow(`${pm} install`)}`); console.log(` ${chalk.yellow(`${pm} run dev`)}`); if (includeBackend) console.log(chalk.cyan(" Backend available at http://localhost:4000")); console.log(chalk.cyan(" Or run both via Docker: docker-compose up")); }