UNPKG

okai

Version:

AI-powered code generation tool for ServiceStack Apps. Generate TypeScript data models, C# APIs, migrations, and UI components from natural language prompts using LLMs.

174 lines 7.35 kB
import { unwrap } from "./cs-ast.js"; import { leftPart, rightPart } from "./utils.js"; export class CSharpGenerator { namespaces = []; apis = []; classes = []; enums = []; ast = { namespaces: [], operations: [], types: [] }; typeFilter; typeHasAttr(type, name) { return type.attributes?.some(x => x.name === name); } propHasAttr(prop, name) { return prop.attributes?.some(x => x.name === name); } addNamespace(ns) { if (!ns || this.namespaces.includes(ns)) return; this.namespaces.push(ns); } toTypeRef(typeRef) { return this.toType(typeRef.name, typeRef.genericArgs, typeRef.namespace); } toType(name, genericArgs, namespace) { if (namespace) { this.addNamespace(namespace); } const optional = name.endsWith('?'); return genericArgs?.length ? `${unwrap(leftPart(name, '`'))}<${genericArgs.join(',')}>` + (optional ? '?' : '') : name; } sortAttributes(annotations) { const to = annotations.sort((x, y) => { const prefix = ['Read.', 'Create.', 'Update.', 'Delete.', 'Write.']; const xPrefix = prefix.findIndex(p => x.name.startsWith(p)); const yPrefix = prefix.findIndex(p => y.name.startsWith(p)); // Sort by Prefix first in order ['Read.','Write.','Update.','Delete.'] if (xPrefix !== yPrefix) return xPrefix == -1 ? 1 : yPrefix == -1 ? -1 : xPrefix - yPrefix; const xName = x.name.includes('.') ? rightPart(x.name, '.') : x.name; const yName = y.name.includes('.') ? rightPart(y.name, '.') : y.name; // then Sort by length of attr name if (xName.length !== yName.length) return xName.length - yName.length; // then Sort by attr name if (xName != yName) return xName.localeCompare(yName); // then Sort by length of constructorArgs[0] if ((x.constructorArgs?.length ?? 0) > 0 && (y.constructorArgs?.length ?? 0) > 0) return x.constructorArgs[0].name.length - y.constructorArgs[0].name.length; // then Sort by constructorArgs.length if (x.constructorArgs?.length !== y.constructorArgs?.length) return (x.constructorArgs?.length ?? 0) - (y.constructorArgs?.length ?? 0); // then Sort by args.length return (x.args?.length ?? 0) - (y.args?.length ?? 0); }); return to; } toAttribute(attr) { let body = ''; if (attr.constructorArgs?.length) { for (const arg of attr.constructorArgs) { this.addNamespace(arg.namespace); if (body) body += ','; const value = arg.type.toLowerCase() === 'string' ? this.toQuotedString(arg.value) : arg.value; body += value; } } if (attr.args?.length) { for (const arg of attr.args) { this.addNamespace(arg.namespace); if (body) body += ','; const value = arg.type.toLowerCase() === 'string' ? this.toQuotedString(arg.value) : arg.value; body += `${arg.name}=${value}`; } } return `[${attr.name}${body ? `(${body})` : ''}]`; } toClass(cls, opt) { const showDesc = !opt || !opt.hideAttrs?.includes('description'); const sb = []; if (this.typeFilter?.before) { this.typeFilter.before(cls, sb); } let clsDef = `public class ${cls.name}`; if (cls.inherits) { this.addNamespace(cls.inherits.namespace); clsDef += ` : ${this.toTypeRef(cls.inherits)}`; } if (cls.implements?.length) { clsDef += (cls.inherits ? ', ' : ' : ') + `${cls.implements.map(x => this.toTypeRef(x)).join(', ')}`; } if (showDesc && cls.description) { sb.push(`/// <summary>`); sb.push(`/// ${cls.description.replace(/\n/g, '\n/// ')}`); sb.push(`/// </summary>`); } for (const attr of this.sortAttributes(cls.attributes ?? [])) { if (opt?.hideAttrs?.includes(attr.name.toLowerCase())) continue; const def = this.toAttribute(attr); sb.push(`${def}`); } sb.push(clsDef); sb.push('{'); for (const prop of cls.properties ?? []) { if (opt?.ignoreProp?.(prop)) continue; this.addNamespace(prop.namespace); if (showDesc && prop.description) { sb.push(` /// <summary>`); sb.push(` /// ${prop.description.replace(/\n/g, '\n /// ')}`); sb.push(` /// </summary>`); } for (const attr of this.sortAttributes(prop.attributes ?? [])) { if (opt?.hideAttrs?.includes(attr.name.toLowerCase())) continue; const def = this.toAttribute(attr); sb.push(` ${def}`); } const propType = this.toType(prop.type, prop.genericArgs, prop.namespace); sb.push(` public ${propType} ${prop.name} { get; set; }`); } sb.push('}'); if (this.typeFilter?.after) { this.typeFilter.after(cls, sb); } return sb.join('\n'); } toQuotedString(s, defaultValue = '""') { return s ? `"${s.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('\n', '\\n').replaceAll('\t', '\\t')}"` : defaultValue; } toEnum(enumType, opt) { const showDesc = !opt || !opt.hideAttrs?.includes('description'); const sb = []; if (showDesc && enumType.description) { sb.push(`[Description(${this.toQuotedString(enumType.description)})]`); } sb.push(`public enum ${enumType.name}`); sb.push('{'); if (enumType.enumNames?.length) { const usesDefaultValues = enumType.enumValues?.length == enumType.enumNames.length && enumType.enumValues.every((x, i) => x === i.toString()); for (let i = 0; i < enumType.enumNames.length; i++) { const name = enumType.enumNames[i]; let value = enumType.enumValues?.[i]; let memberValue = enumType.enumMemberValues && i < enumType.enumMemberValues.length ? enumType.enumMemberValues[i] : value; let desc = enumType.enumDescriptions?.[i]; if (desc) { sb.push(` [Description(${this.toQuotedString(desc)})]`); } if (memberValue && memberValue !== value && memberValue !== name) { sb.push(` [EnumMember(Value = ${this.toQuotedString(memberValue)})]`); } sb.push(value && !usesDefaultValues ? ` ${name} = ${value},` : ` ${name},`); } } sb.push('}'); return sb.join('\n'); } generate(ast) { } } //# sourceMappingURL=cs-gen.js.map