Skip to content

Commit 17ba6bc

Browse files
stephentoubCopilot
andcommitted
Align rebased ref generator changes
Keep the rebased $ref generator follow-up aligned with the latest C# typing changes and clean up the Python/TypeScript generator adjustments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 31087ed commit 17ba6bc

File tree

3 files changed

+52
-74
lines changed

3 files changed

+52
-74
lines changed

scripts/codegen/csharp.ts

Lines changed: 47 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
getApiSchemaPath,
1717
writeGeneratedFile,
1818
collectDefinitions,
19+
postProcessSchema,
1920
resolveRef,
2021
refTypeName,
2122
isRpcMethod,
@@ -512,18 +513,25 @@ function resolveSessionPropertyType(
512513
): string {
513514
// Handle $ref by resolving against schema definitions
514515
if (propSchema.$ref) {
515-
const typeName = refTypeName(propSchema.$ref);
516-
const className = typeToClassName(typeName);
517-
if (!nestedClasses.has(className)) {
518-
const refSchema = resolveRef(propSchema.$ref, sessionDefinitions);
519-
if (refSchema) {
520-
if (refSchema.enum && Array.isArray(refSchema.enum)) {
521-
return getOrCreateEnum(className, "", refSchema.enum as string[], enumOutput);
522-
}
516+
const className = typeToClassName(refTypeName(propSchema.$ref));
517+
const refSchema = resolveRef(propSchema.$ref, sessionDefinitions);
518+
if (!refSchema) {
519+
return isRequired ? className : `${className}?`;
520+
}
521+
522+
if (refSchema.enum && Array.isArray(refSchema.enum)) {
523+
const enumName = getOrCreateEnum(className, "", refSchema.enum as string[], enumOutput, refSchema.description);
524+
return isRequired ? enumName : `${enumName}?`;
525+
}
526+
527+
if (refSchema.type === "object" && refSchema.properties) {
528+
if (!nestedClasses.has(className)) {
523529
nestedClasses.set(className, generateNestedClass(className, refSchema, knownTypes, nestedClasses, enumOutput));
524530
}
531+
return isRequired ? className : `${className}?`;
525532
}
526-
return isRequired ? className : `${className}?`;
533+
534+
return resolveSessionPropertyType(refSchema, parentClassName, propName, isRequired, knownTypes, nestedClasses, enumOutput);
527535
}
528536
if (propSchema.anyOf) {
529537
const hasNull = propSchema.anyOf.some((s) => typeof s === "object" && (s as JSONSchema7).type === "null");
@@ -556,40 +564,15 @@ function resolveSessionPropertyType(
556564
}
557565
if (propSchema.type === "array" && propSchema.items) {
558566
const items = propSchema.items as JSONSchema7;
559-
// Handle $ref in array items
560-
if (items.$ref) {
561-
const typeName = refTypeName(items.$ref);
562-
const className = typeToClassName(typeName);
563-
if (!nestedClasses.has(className)) {
564-
const refSchema = resolveRef(items.$ref, sessionDefinitions);
565-
if (refSchema) {
566-
nestedClasses.set(className, generateNestedClass(className, refSchema, knownTypes, nestedClasses, enumOutput));
567-
}
568-
}
569-
return isRequired ? `${className}[]` : `${className}[]?`;
570-
}
571-
// Array of discriminated union (anyOf with shared discriminator)
572-
if (items.anyOf && Array.isArray(items.anyOf)) {
573-
const variants = items.anyOf.filter((v): v is JSONSchema7 => typeof v === "object");
574-
const discriminatorInfo = findDiscriminator(variants);
575-
if (discriminatorInfo) {
576-
const baseClassName = `${parentClassName}${propName}Item`;
577-
const renamedBase = applyTypeRename(baseClassName);
578-
const polymorphicCode = generatePolymorphicClasses(baseClassName, discriminatorInfo.property, variants, knownTypes, nestedClasses, enumOutput, items.description);
579-
nestedClasses.set(renamedBase, polymorphicCode);
580-
return isRequired ? `${renamedBase}[]` : `${renamedBase}[]?`;
581-
}
582-
}
583-
if (items.type === "object" && items.properties) {
584-
const itemClassName = `${parentClassName}${propName}Item`;
585-
nestedClasses.set(itemClassName, generateNestedClass(itemClassName, items, knownTypes, nestedClasses, enumOutput));
586-
return isRequired ? `${itemClassName}[]` : `${itemClassName}[]?`;
587-
}
588-
if (items.enum && Array.isArray(items.enum)) {
589-
const enumName = getOrCreateEnum(parentClassName, `${propName}Item`, items.enum as string[], enumOutput, items.description);
590-
return isRequired ? `${enumName}[]` : `${enumName}[]?`;
591-
}
592-
const itemType = schemaTypeToCSharp(items, true, knownTypes);
567+
const itemType = resolveSessionPropertyType(
568+
items,
569+
parentClassName,
570+
`${propName}Item`,
571+
true,
572+
knownTypes,
573+
nestedClasses,
574+
enumOutput
575+
);
593576
return isRequired ? `${itemType}[]` : `${itemType}[]?`;
594577
}
595578
return schemaTypeToCSharp(propSchema, isRequired, knownTypes);
@@ -725,7 +708,8 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
725708
console.log("C#: generating session-events...");
726709
const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath());
727710
const schema = JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as JSONSchema7;
728-
const code = generateSessionEventsCode(schema);
711+
const processed = postProcessSchema(schema);
712+
const code = generateSessionEventsCode(processed);
729713
const outPath = await writeGeneratedFile("dotnet/src/Generated/SessionEvents.cs", code);
730714
console.log(` ✓ ${outPath}`);
731715
await formatCSharpFile(outPath);
@@ -773,13 +757,24 @@ function stableStringify(value: unknown): string {
773757
function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassName: string, propName: string, classes: string[]): string {
774758
// Handle $ref by resolving against schema definitions and generating the referenced class
775759
if (schema.$ref) {
776-
const typeName = refTypeName(schema.$ref);
760+
const typeName = typeToClassName(refTypeName(schema.$ref));
777761
const refSchema = resolveRef(schema.$ref, rpcDefinitions);
778-
if (refSchema && !emittedRpcClasses.has(typeName)) {
762+
if (!refSchema) {
763+
return isRequired ? typeName : `${typeName}?`;
764+
}
765+
766+
if (refSchema.enum && Array.isArray(refSchema.enum)) {
767+
const enumName = getOrCreateEnum(typeName, "", refSchema.enum as string[], rpcEnumOutput, refSchema.description);
768+
return isRequired ? enumName : `${enumName}?`;
769+
}
770+
771+
if (refSchema.type === "object" && refSchema.properties) {
779772
const cls = emitRpcClass(typeName, refSchema, "public", classes);
780773
if (cls) classes.push(cls);
774+
return isRequired ? typeName : `${typeName}?`;
781775
}
782-
return isRequired ? typeName : `${typeName}?`;
776+
777+
return resolveRpcType(refSchema, isRequired, parentClassName, propName, classes);
783778
}
784779
// Handle anyOf: [T, null] → T? (nullable typed property)
785780
if (schema.anyOf) {
@@ -801,32 +796,17 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
801796
}
802797
if (schema.type === "array" && schema.items) {
803798
const items = schema.items as JSONSchema7;
804-
// Handle $ref in array items
805-
if (items.$ref) {
806-
const typeName = refTypeName(items.$ref);
807-
const refSchema = resolveRef(items.$ref, rpcDefinitions);
808-
if (refSchema && !emittedRpcClasses.has(typeName)) {
809-
const cls = emitRpcClass(typeName, refSchema, "public", classes);
810-
if (cls) classes.push(cls);
811-
}
812-
return isRequired ? `List<${typeName}>` : `List<${typeName}>?`;
813-
}
814799
if (items.type === "object" && items.properties) {
815800
const itemClass = (items.title as string) ?? singularPascal(propName);
816801
classes.push(emitRpcClass(itemClass, items, "public", classes));
817802
return isRequired ? `IList<${itemClass}>` : `IList<${itemClass}>?`;
818803
}
819-
const itemType = schemaTypeToCSharp(items, true, rpcKnownTypes);
804+
const itemType = resolveRpcType(items, true, parentClassName, `${propName}Item`, classes);
820805
return isRequired ? `IList<${itemType}>` : `IList<${itemType}>?`;
821806
}
822807
if (schema.type === "object" && schema.additionalProperties && typeof schema.additionalProperties === "object") {
823808
const vs = schema.additionalProperties as JSONSchema7;
824-
if (vs.type === "object" && vs.properties) {
825-
const valClass = `${parentClassName}${propName}Value`;
826-
classes.push(emitRpcClass(valClass, vs, "public", classes));
827-
return isRequired ? `IDictionary<string, ${valClass}>` : `IDictionary<string, ${valClass}>?`;
828-
}
829-
const valueType = schemaTypeToCSharp(vs, true, rpcKnownTypes);
809+
const valueType = resolveRpcType(vs, true, parentClassName, `${propName}Value`, classes);
830810
return isRequired ? `IDictionary<string, ${valueType}>` : `IDictionary<string, ${valueType}>?`;
831811
}
832812
return schemaTypeToCSharp(schema, isRequired, rpcKnownTypes);
@@ -1007,15 +987,9 @@ function emitServerInstanceMethod(
1007987
if (typeof pSchema !== "object") continue;
1008988
const isReq = requiredSet.has(pName);
1009989
const jsonSchema = pSchema as JSONSchema7;
1010-
let csType: string;
1011-
// If the property has an enum, resolve to the generated enum type
1012-
if (jsonSchema.enum && Array.isArray(jsonSchema.enum) && requestClassName) {
1013-
const valuesKey = [...jsonSchema.enum].sort().join("|");
1014-
const match = [...generatedEnums.values()].find((e) => [...e.values].sort().join("|") === valuesKey);
1015-
csType = match ? (isReq ? match.enumName : `${match.enumName}?`) : schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
1016-
} else {
1017-
csType = schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
1018-
}
990+
const csType = requestClassName
991+
? resolveRpcType(jsonSchema, isReq, requestClassName, toPascalCase(pName), classes)
992+
: schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
1019993
sigParams.push(`${csType} ${pName}${isReq ? "" : " = null"}`);
1020994
bodyAssignments.push(`${toPascalCase(pName)} = ${pName}`);
1021995
}

scripts/codegen/python.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
postProcessSchema,
1717
writeGeneratedFile,
1818
collectDefinitions,
19-
isRpcMethod,
2019
isNodeFullyExperimental,
2120
type ApiSchema,
2221
type RpcMethod,

scripts/codegen/typescript.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,12 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js";
125125

126126
// Strip the placeholder root type and keep only the definition-generated types
127127
const strippedTs = compiled
128+
.replace(
129+
/\/\*\*\n \* This (?:interface|type) was referenced by `_RpcSchemaRoot`'s JSON-Schema\n \* via the `definition` "[^"]+"\.\n \*\/\n/g,
130+
"\n"
131+
)
128132
.replace(/export interface _RpcSchemaRoot\s*\{[^}]*\}\s*/g, "")
133+
.replace(/export type _RpcSchemaRoot = [^;]+;\s*/g, "")
129134
.trim();
130135

131136
if (strippedTs) {

0 commit comments

Comments
 (0)