184 lines
4.8 KiB
JavaScript
184 lines
4.8 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'
|
|
parents?: string[]
|
|
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 === 'array' ? '[]' : field.type};`)
|
|
.join('\n')
|
|
const parents = apiType.parents || []
|
|
const extendsClause = parents.length > 0 ? ` extends ${parents.join(', ')}` : ''
|
|
return `export interface ${apiType.name}${extendsClause} {
|
|
${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 }
|