UNPKG

vibe-cli-tool

Version:

VibeCLI v1.9 - 模板商店生态,智能部署生成,多语言支持,交互式学习系统,企业级Web全栈应用CLI工具

144 lines 6.41 kB
import fs from 'fs-extra'; import path from 'path'; import crypto from 'crypto'; /** * Generate a random ID deterministic per project (hashed path). */ function getStableId(seed) { return crypto.createHash('sha1').update(seed).digest('hex').slice(0, 10); } export async function generateDeploymentArtifacts(projectPath, config) { switch (config.platform) { case 'vercel': return generateVercelArtifacts(projectPath, config); case 'docker': return generateDockerArtifacts(projectPath, config); case 'netlify': return generateNetlifyArtifacts(projectPath, config); case 'cloudflare': return generateCloudflareArtifacts(projectPath, config); case 'aliyun': return generateAliyunArtifacts(projectPath, config); case 'aws': default: // Not yet implemented return { deploymentId: `deploy_${Date.now()}`, }; } } async function generateVercelArtifacts(projectPath, config) { const vercelDir = path.join(projectPath, '.vercel'); await fs.ensureDir(vercelDir); const projectId = getStableId(projectPath); const orgId = getStableId('org'); // Basic project.json const projectJson = { projectId, orgId, settings: { framework: 'nextjs', devCommand: 'next dev', buildCommand: 'npm run build', outputDirectory: 'out', installCommand: 'npm install', }, }; await fs.writeJson(path.join(vercelDir, 'project.json'), projectJson, { spaces: 2, }); // output.json for static export (if any) const outputJsonPath = path.join(vercelDir, 'output.json'); if (!(await fs.pathExists(outputJsonPath))) { await fs.writeJson(outputJsonPath, { version: 3, routes: [], }, { spaces: 2 }); } // migration for sqlite → blob (only if sqlite detected) await maybeGenerateSqliteMigration(projectPath); const deploymentId = `vercel_${Date.now()}`; const url = `https://${projectId}-vibecli.vercel.app`; await writeDeploymentMeta(projectPath, config, deploymentId, url); return { deploymentId, url }; } async function generateDockerArtifacts(projectPath, config) { const dockerfilePath = path.join(projectPath, 'Dockerfile'); if (!(await fs.pathExists(dockerfilePath))) { const dockerfileContent = `# Generated by VibeCLI\nFROM node:20-alpine AS builder\nWORKDIR /app\nCOPY . .\nRUN npm ci && npm run build\n\nFROM node:20-alpine\nWORKDIR /app\nCOPY --from=builder /app .\nEXPOSE 3000\nCMD [\"npm\", \"run\", \"start\"]\n`; await fs.writeFile(dockerfilePath, dockerfileContent); } await maybeGenerateSqliteMigration(projectPath); const deploymentId = `docker_${Date.now()}`; await writeDeploymentMeta(projectPath, config, deploymentId); return { deploymentId }; } async function generateNetlifyArtifacts(projectPath, config) { const netlifyConfigPath = path.join(projectPath, 'netlify.toml'); if (!(await fs.pathExists(netlifyConfigPath))) { const tomlContent = `# Generated by VibeCLI\n[build]\n command = \"npm run build\"\n publish = \"out\"\n`; await fs.writeFile(netlifyConfigPath, tomlContent); } await maybeGenerateSqliteMigration(projectPath); const deploymentId = `netlify_${Date.now()}`; await writeDeploymentMeta(projectPath, config, deploymentId); return { deploymentId }; } async function generateCloudflareArtifacts(projectPath, config) { const wranglerPath = path.join(projectPath, 'wrangler.toml'); if (!(await fs.pathExists(wranglerPath))) { const content = `# Generated by VibeCLI\nname = "${path.basename(projectPath)}"\naccount_id = "YOUR_CLOUDFLARE_ACCOUNT_ID"\ncompatibility_date = "${new Date().toISOString().slice(0, 10)}"\npages_build_output_dir = "out"\n`; await fs.writeFile(wranglerPath, content); } // Static export expects output dir 'out' await maybeGenerateSqliteMigration(projectPath); const deploymentId = `cf_${Date.now()}`; const url = `https://${path.basename(projectPath)}.pages.dev`; await writeDeploymentMeta(projectPath, config, deploymentId, url); return { deploymentId, url }; } async function generateAliyunArtifacts(projectPath, config) { const fcDir = path.join(projectPath, 'aliyun'); await fs.ensureDir(fcDir); const readmePath = path.join(fcDir, 'README.md'); if (!(await fs.pathExists(readmePath))) { const md = `# Aliyun 部署\n\n此目录包含使用 Aliyun Function Compute / 容器镜像服务部署的占位文件。\n\n> TODO: 根据项目需要自定义 serverless.yml 或 Dockerfile 并使用 \`aliyun cli\` 或 \`terraform\` 部署。`; await fs.writeFile(readmePath, md); } await maybeGenerateSqliteMigration(projectPath); const deploymentId = `aliyun_${Date.now()}`; await writeDeploymentMeta(projectPath, config, deploymentId); return { deploymentId }; } async function maybeGenerateSqliteMigration(projectPath) { try { const schemaPath = path.join(projectPath, 'prisma', 'schema.prisma'); if (await fs.pathExists(schemaPath)) { const schema = await fs.readFile(schemaPath, 'utf-8'); if (schema.includes('provider = "sqlite"')) { const scriptPath = path.join(projectPath, 'scripts', 'migrate-sqlite-to-blob.js'); if (!(await fs.pathExists(scriptPath))) { await fs.ensureDir(path.dirname(scriptPath)); const content = `#!/usr/bin/env node\n// Simple placeholder migration script\nconsole.log('TODO: migrate SQLite data to Vercel Blob');\n`; await fs.writeFile(scriptPath, content); await fs.chmod(scriptPath, 0o755); } } } } catch { // ignore } } async function writeDeploymentMeta(projectPath, config, deploymentId, url) { const metaPath = path.join(projectPath, 'deployment.json'); const meta = { platform: config.platform, environment: config.environment, deploymentId, url: url ?? null, generatedAt: new Date().toISOString(), }; await fs.writeJson(metaPath, meta, { spaces: 2 }); } //# sourceMappingURL=deploy-generator.js.map