import fs from 'node:fs/promises'; import path from 'node:path'; const projectRoot = process.cwd(); const distDir = path.join(projectRoot, 'dist'); const deployDir = path.join(projectRoot, 'deploy_bundle'); const deployWebDir = path.join(deployDir, 'web'); const deployServerDir = path.join(deployDir, 'server'); const exists = async (p) => { try { await fs.access(p); return true; } catch { return false; } }; const ensureDir = async (p) => { await fs.mkdir(p, { recursive: true }); }; const emptyDir = async (p) => { await fs.rm(p, { recursive: true, force: true }); await fs.mkdir(p, { recursive: true }); }; const copyDir = async (src, dest) => { await fs.cp(src, dest, { recursive: true }); }; const copyFile = async (src, dest) => { await ensureDir(path.dirname(dest)); await fs.copyFile(src, dest); }; if (!(await exists(distDir))) { throw new Error('未找到 dist/。请先执行:npm run build'); } // 1) web: dist/ 下除 api/ 之外所有内容 await emptyDir(deployWebDir); const distEntries = await fs.readdir(distDir, { withFileTypes: true }); for (const entry of distEntries) { if (entry.name === 'api') continue; const src = path.join(distDir, entry.name); const dest = path.join(deployWebDir, entry.name); if (entry.isDirectory()) { await copyDir(src, dest); } else { await copyFile(src, dest); } } // 2) server: dist/api -> deploy_bundle/server/dist/api await ensureDir(deployServerDir); await ensureDir(path.join(deployServerDir, 'dist')); const distApiDir = path.join(distDir, 'api'); if (!(await exists(distApiDir))) { throw new Error('未找到 dist/api/。请确认 npm run build 已成功执行(包含 scripts/build-api.mjs)。'); } await emptyDir(path.join(deployServerDir, 'dist', 'api')); await copyDir(distApiDir, path.join(deployServerDir, 'dist', 'api')); // 3) 同步 server 运行所需文件 await copyFile(path.join(projectRoot, 'package.json'), path.join(deployServerDir, 'package.json')); if (await exists(path.join(projectRoot, 'package-lock.json'))) { await copyFile(path.join(projectRoot, 'package-lock.json'), path.join(deployServerDir, 'package-lock.json')); } // ecosystem.config.cjs:以仓库根目录为准同步(如需环境差异,可在服务器上覆盖 env/cwd) if (await exists(path.join(projectRoot, 'ecosystem.config.cjs'))) { await copyFile(path.join(projectRoot, 'ecosystem.config.cjs'), path.join(deployServerDir, 'ecosystem.config.cjs')); } // sanity check const initSql = path.join(deployServerDir, 'dist', 'api', 'database', 'init.sql'); if (!(await exists(initSql))) { throw new Error('缺少 deploy_bundle/server/dist/api/database/init.sql(postbuild 应该会复制)。'); } console.log('deploy_bundle 已生成:'); console.log('- deploy_bundle/web'); console.log('- deploy_bundle/server');