x-control-panel/web-ui/vue-app/scripts/generate-rpc-client.ts

179 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
interface ApiMethodParam {
name: string
type: string
description: string
optional: boolean
}
interface ApiMethod {
name: string
description: string
return: string
accessLevel: string
params: ApiMethodParam[]
}
interface ApiModule {
name: string
methods: ApiMethod[]
}
interface ApiType {
name: string
type: 'class' | 'enum'
fields?: Array<{
name: string
type: string
}>
values?: string[]
}
interface ApiSpec {
modules: ApiModule[]
types: ApiType[]
}
function generateMethod(method: ApiMethod): string {
const sortedParams = [...method.params].sort((a, b) => {
if (a.optional && !b.optional) return 1
if (!a.optional && b.optional) return -1
return 0
})
const params = sortedParams
.map((param) => {
const optional = param.optional ? '?' : ''
return `${param.name}${optional}: ${param.type}`
})
.join(', ')
const callParams = method.params.map((param) => `${param.name}: ${param.name}`).join(', ')
return `
/**
* @description ${method.description}
${sortedParams.map((param) => ` * @param ${param.name} ${param.description}`).join('\n')}
* @return ${method.return}
*/
async ${method.name}(${params}): Promise<${method.return}> {
return this.call("${method.name}"${callParams ? ', {' + callParams + '}' : ''});
}`
}
function generateType(apiType: ApiType): string {
if (apiType.type === 'enum') {
const values = apiType.values || []
return `export enum ${apiType.name} {
${values.map((value) => ` ${value} = "${value}"`).join(',\n')}
}`
} else if (apiType.type === 'class') {
const fields = apiType.fields || []
const fieldDefinitions = fields.map((field) => ` ${field.name}: ${field.type};`).join('\n')
return `export interface ${apiType.name} {
${fieldDefinitions}
}`
}
return ''
}
function generateTypes(spec: ApiSpec): string {
const types = spec.types.map(generateType).join('\n\n')
return types ? `\n// Generated Types\n${types}` : ''
}
function generateModule(module: ApiModule): string {
const className = `${module.name}Module`
const methods = module.methods.map(generateMethod).join('\n')
return `export class ${className} extends RpcModuleBase {
constructor(client: RpcClientBase) {
super(client, "${module.name}");
}${methods}
}`
}
function generateClient(spec: ApiSpec): string {
const modules = spec.modules
.map((module) => {
const moduleName = module.name
const className = `${module.name}Module`
return ` public ${moduleName}: ${className};`
})
.join('\n')
const initializations = spec.modules
.map((module) => {
const moduleName = module.name
const className = `${module.name}Module`
return ` this.${moduleName} = new ${className}(this);`
})
.join('\n')
const moduleClasses = spec.modules.map(generateModule).join('\n\n')
const types = generateTypes(spec)
return `import {RpcClientBase} from "@/api/RpcClientBase.ts";
import {RpcModuleBase} from "@/api/RpcModuleBase.ts";
${types}
export class RpcClient extends RpcClientBase {${modules}
constructor(baseUrl: string) {
super(baseUrl);
${initializations}
}
}
${moduleClasses}`
}
function main() {
const specPath = path.resolve(__dirname, '../../../api.spec.json')
const outputPath = path.resolve(__dirname, '../src/generated/RpcClient.ts')
console.log('🚀 Generating RPC client...')
try {
if (!fs.existsSync(specPath)) {
throw new Error(`API spec file not found: ${specPath}`)
}
const specContent = fs.readFileSync(specPath, 'utf-8')
const spec: ApiSpec = JSON.parse(specContent)
const clientCode = generateClient(spec)
const outputDir = path.dirname(outputPath)
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true })
}
fs.writeFileSync(outputPath, clientCode)
console.log(`✅ RPC client generated successfully: ${outputPath}`)
console.log(`📋 Generated ${spec.modules.length} API modules`)
if (spec.types.length > 0) {
console.log(
`📝 Generated ${spec.types.length} types (${spec.types.filter((t) => t.type === 'class').length} interfaces, ${spec.types.filter((t) => t.type === 'enum').length} enums)`
)
}
} catch (error) {
console.error('❌ Error generating RPC client:', error)
process.exit(1)
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main()
}
export { main as generateClient }