yx_generate_api_js/src/commands/generate.js

173 lines
5.1 KiB
JavaScript

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()
}
}
}