/** * 简化的 API 生成脚本 * * 这个脚本现在主要负责: * 1. 使用 @hey-api/openapi-ts 命令生成客户端代码 * 2. 环境配置通过 openapi-ts.config.ts 处理 */ const fs = require('fs'); const path = require('path'); // ANSI 颜色代码 const colors = { reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m' }; // 日志函数 function log(message, color = 'white') { console.log(`${colors[color]}${message}${colors.reset}`); } function logSuccess(message) { log(`✓ ${message}`, 'green'); } function logError(message) { log(`✗ ${message}`, 'red'); } function logWarning(message) { log(`⚠ ${message}`, 'yellow'); } function logInfo(message) { log(`ℹ ${message}`, 'blue'); } // 显示环境配置信息 logInfo(`当前环境: ${process.env.NODE_ENV || 'development'}`); logInfo(`API 服务器: ${process.env.API_BASE_URL || 'http://localhost:8080'}`); /** * 检查自定义文件是否存在 * 如果某些文件已经被自定义,我们不希望覆盖它们 */ function checkCustomFiles() { const customFiles = [ 'client.gen.ts' // 客户端文件通常是自定义的 ]; const outputDir = path.join(process.cwd(), 'src', 'lib', 'api'); const existingCustomFiles = []; for (const file of customFiles) { const filePath = path.join(outputDir, file); if (fs.existsSync(filePath)) { // 检查文件是否包含自定义内容的标识 const content = fs.readFileSync(filePath, 'utf8'); // 检查是否有自定义配置的标识 const customIndicators = [ 'getBaseUrl', 'createDynamicClient', 'getCurrentClientConfig', // 可以添加更多自定义标识 ]; const hasCustomContent = customIndicators.some(indicator => content.includes(indicator) ); if (hasCustomContent) { existingCustomFiles.push(file); logWarning(`检测到自定义文件: ${file}`); } } } return existingCustomFiles; } /** * 备份自定义文件 */ function backupCustomFiles(customFiles) { if (customFiles.length === 0) return; logInfo('备份自定义文件...'); const outputDir = path.join(process.cwd(), 'src', 'lib', 'api'); const backupDir = path.join(process.cwd(), 'src', 'lib', 'api', 'backup'); // 创建备份目录 if (!fs.existsSync(backupDir)) { fs.mkdirSync(backupDir, { recursive: true }); } const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const timestampedBackupDir = path.join(backupDir, `backup-${timestamp}`); fs.mkdirSync(timestampedBackupDir); for (const file of customFiles) { const srcPath = path.join(outputDir, file); const destPath = path.join(timestampedBackupDir, file); fs.copyFileSync(srcPath, destPath); logInfo(` 备份: ${file} -> backup/${timestamp}/${file}`); } logSuccess(`自定义文件已备份到: backup/${timestamp}/`); } /** * 恢复自定义文件 */ function restoreCustomFiles(customFiles) { if (customFiles.length === 0) return; logInfo('恢复自定义文件...'); const outputDir = path.join(process.cwd(), 'src', 'lib', 'api'); const backupDir = path.join(process.cwd(), 'src', 'lib', 'api', 'backup'); // 找到最新的备份目录 const backupDirs = fs.existsSync(backupDir) ? fs.readdirSync(backupDir) .filter(name => name.startsWith('backup-')) .sort() .reverse() : []; if (backupDirs.length === 0) { logWarning('没有找到备份文件,跳过恢复'); return; } const latestBackupDir = path.join(backupDir, backupDirs[0]); for (const file of customFiles) { const srcPath = path.join(latestBackupDir, file); const destPath = path.join(outputDir, file); if (fs.existsSync(srcPath)) { fs.copyFileSync(srcPath, destPath); logInfo(` 恢复: ${file}`); } } logSuccess('自定义文件已恢复'); } /** * 使用 openapi-ts 命令生成客户端代码 */ function generateWithOpenApiTS() { return new Promise((resolve, reject) => { logInfo('使用 @hey-api/openapi-ts 生成客户端代码...'); const { exec } = require('child_process'); const command = 'npx @hey-api/openapi-ts'; logInfo(`执行命令: ${command}`); const startTime = Date.now(); exec(command, { cwd: process.cwd() }, (error, stdout, stderr) => { const executionTime = Date.now() - startTime; if (error) { logError(`openapi-ts 执行失败 (${executionTime}ms)`); logError(`错误信息: ${error.message}`); if (stderr) { logError(`stderr: ${stderr}`); } reject(error); return; } if (stderr) { logWarning(`stderr: ${stderr}`); } logSuccess(`openapi-ts 执行成功 (${executionTime}ms)`); if (stdout) { logInfo(`输出: ${stdout}`); } resolve(); }); }); } /** * 验证生成的文件 */ function validateGeneratedFiles() { try { logInfo('验证生成的文件...'); const outputDir = path.join(process.cwd(), 'src', 'lib', 'api'); if (!fs.existsSync(outputDir)) { throw new Error('输出目录不存在'); } const files = fs.readdirSync(outputDir); logInfo(`输出目录包含 ${files.length} 个文件:`); const requiredFiles = ['types.gen.ts', 'sdk.gen.ts', 'index.ts']; const generatedFiles = []; for (const file of files) { const filePath = path.join(outputDir, file); const stats = fs.statSync(filePath); // 只显示文件,不显示目录 if (stats.isFile()) { const size = (stats.size / 1024).toFixed(2); logInfo(` 📄 ${file} (${size} KB)`); generatedFiles.push(file); } } // 检查必要文件 const missingFiles = requiredFiles.filter(file => !generatedFiles.includes(file)); if (missingFiles.length > 0) { logWarning(`缺少期望的文件: ${missingFiles.join(', ')}`); } else { logSuccess('所有期望的文件都已生成'); } return true; } catch (error) { logError(`验证生成的文件失败: ${error.message}`); return false; } } /** * 主函数 */ async function main() { const startTime = Date.now(); log('='.repeat(60), 'cyan'); log('API 客户端代码生成脚本', 'cyan'); log('基于 @hey-api/openapi-ts', 'cyan'); log('='.repeat(60), 'cyan'); try { // 检查是否有自定义文件 const customFiles = checkCustomFiles(); if (customFiles.length > 0) { logInfo('检测到自定义文件,将在生成前进行备份'); backupCustomFiles(customFiles); } // 使用 openapi-ts 生成代码 await generateWithOpenApiTS(); // 恢复自定义文件 if (customFiles.length > 0) { restoreCustomFiles(customFiles); } // 验证生成的文件 if (!validateGeneratedFiles()) { logWarning('文件验证发现问题,但生成过程已完成'); } const totalTime = Date.now() - startTime; logSuccess(`API 客户端代码生成完成!总耗时: ${totalTime}ms`); logSuccess('生成的文件位于 src/lib/api/ 目录'); if (customFiles.length > 0) { logInfo('自定义文件已保持不变,避免覆盖手动配置'); } } catch (error) { const totalTime = Date.now() - startTime; logError(`脚本执行失败 (${totalTime}ms): ${error.message}`); process.exit(1); } } // 执行主函数 if (require.main === module) { main(); }