import process from 'node:process' import { createInterface } from 'node:readline/promises' import { parseCliArgs, getFlagValue, getFlagValues, hasFlag } from '../core/args.js' import { loadProjectConfig, normalizeParamStyle, resolveCwdPath, resolveSwaggerSource, } from '../core/config.js' import { generateApiFiles } from '../core/generate.js' export const runGenerateCommand = async (args) => { const context = await resolveGenerateCommandContext(args) if (context.help) { printHelp() return } await generateApiFiles(context.runtimeConfig) } export const resolveGenerateCommandContext = async (args) => { const parsedArgs = parseCliArgs(args) const help = hasFlag(parsedArgs.flags, 'help') || hasFlag(parsedArgs.flags, 'h') if (help) { return { help: true, } } const configPath = getFlagValue(parsedArgs.flags, 'config') const projectConfig = await loadProjectConfig({ configPath, cwd: process.cwd(), }) const moduleArgs = [ ...parsedArgs.positionals, ...getFlagValues(parsedArgs.flags, 'modules').flatMap(splitModuleArgs), ] return { help: false, projectConfig, runtimeConfig: { projectRoot: projectConfig.rootDir, swaggerUrl: getFlagValue(parsedArgs.flags, 'url') ? resolveSwaggerSource(process.cwd(), getFlagValue(parsedArgs.flags, 'url')) : projectConfig.swaggerUrl, swaggerTimeoutMs: resolveSwaggerTimeoutMs(parsedArgs.flags, projectConfig.swaggerTimeoutMs), outputDir: getFlagValue(parsedArgs.flags, 'outDir') ? resolveCwdPath(process.cwd(), getFlagValue(parsedArgs.flags, 'outDir')) : projectConfig.outputDir, requestImport: getFlagValue(parsedArgs.flags, 'requestImport') || projectConfig.requestImport, paramStyle: getFlagValue(parsedArgs.flags, 'paramStyle') ? normalizeParamStyle(getFlagValue(parsedArgs.flags, 'paramStyle')) : projectConfig.paramStyle, cleanOutput: hasFlag(parsedArgs.flags, 'clean') ? Boolean(getFlagValue(parsedArgs.flags, 'clean')) : projectConfig.cleanOutput, modules: moduleArgs, resolveDuplicateExports: createDuplicateExportResolver(), }, } } const printHelp = () => { console.log(`yx-generate-api generate Usage: yx-generate-api generate [moduleName...] [options] Options: --config=... Config file path --url=... Swagger/OpenAPI JSON URL Supports http(s), file://, or a local JSON file path --outDir=... Output directory --requestImport=... request import path inside generated files --modules=... Comma-separated module list --paramStyle=... object / positional --timeout=... Swagger fetch timeout in milliseconds --clean Remove stale auto-generated module files on full generation --no-clean Keep previously generated module files --help Show help Examples: yx-generate-api generate yx-generate-api generate Curriculum yx-generate-api generate class-assignment Ranking yx-generate-api generate --modules=Curriculum,class-assignment yx-generate-api generate --config ./yx-generate-api.config.mjs --timeout 10000 `) } const splitModuleArgs = (value) => { return String(value || '') .split(',') .map((item) => item.trim()) .filter(Boolean) } const resolveSwaggerTimeoutMs = (flags, fallbackValue) => { const rawValue = getFlagValue(flags, 'timeout') || getFlagValue(flags, 'timeoutMs') if (rawValue === undefined) { return fallbackValue } const parsedValue = Number.parseInt(String(rawValue), 10) if (!Number.isInteger(parsedValue) || parsedValue <= 0) { throw new Error(`--timeout must be a positive integer. Received: ${rawValue}`) } return parsedValue } const createDuplicateExportResolver = () => { if (!process.stdin.isTTY || !process.stdout.isTTY) { return null } return async ({ conflicts }) => { console.log('') console.log('检测到生成后的 API 导出名称冲突:') for (const conflict of conflicts) { console.log(`- 冲突函数:${conflict.exportName}`) for (const occurrence of conflict.occurrences) { const locationText = occurrence.path ? `URL: ${occurrence.path}` : 'URL: 当前保留的已生成模块' console.log( ` 模块: ${occurrence.moduleName} | 文件: ${occurrence.fileName} | ${locationText}`, ) } } console.log('') console.log('请选择处理方式:') console.log('1. 在冲突函数名后追加模块名,例如 getListEnglishWordApi') console.log('2. 退出生成') const readline = createInterface({ input: process.stdin, output: process.stdout, }) try { while (true) { const answer = String(await readline.question('请输入 1 或 2: ')).trim() if (answer === '1') { return { action: 'rename', } } if (answer === '2') { return { action: 'abort', } } console.log('输入无效,请输入 1 或 2。') } } finally { readline.close() } } }