diff --git a/.chronus/changes/copilot-fix-generated-filename-xml-doc-2026-7-7-14-40-43.md b/.chronus/changes/copilot-fix-generated-filename-xml-doc-2026-7-7-14-40-43.md new file mode 100644 index 00000000000..e33bdadcd94 --- /dev/null +++ b/.chronus/changes/copilot-fix-generated-filename-xml-doc-2026-7-7-14-40-43.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-server-csharp" +--- + +Apply C# naming conventions to generated type file names. \ No newline at end of file diff --git a/packages/http-server-csharp/src/components/enums/enums.tsx b/packages/http-server-csharp/src/components/enums/enums.tsx index 8fdcf3d9fa7..581125b8262 100644 --- a/packages/http-server-csharp/src/components/enums/enums.tsx +++ b/packages/http-server-csharp/src/components/enums/enums.tsx @@ -83,6 +83,7 @@ export function Enums(props: EnumsProps): Children { {(info) => { const namePolicy = cs.useCSharpNamePolicy(); + const enumName = namePolicy.getName(info.name, "enum"); const subNsParts = getSubNamespaceParts(info.namespace, props.serviceNamespace); const enumDecl = ( @@ -93,7 +94,7 @@ export function Enums(props: EnumsProps): Children { /> + {wrappedContent} ); diff --git a/packages/http-server-csharp/src/components/models/models.tsx b/packages/http-server-csharp/src/components/models/models.tsx index 4e8bdffe46c..00ffa38e329 100644 --- a/packages/http-server-csharp/src/components/models/models.tsx +++ b/packages/http-server-csharp/src/components/models/models.tsx @@ -64,6 +64,7 @@ export function Models(props: ModelsProps): Children { const needsJsonNodes = modelNeedsJsonNodes($, model); const usings = needsJsonNodes ? [...modelUsings, "System.Text.Json.Nodes"] : modelUsings; const modelName = getModelEmitName($.program, model); + const modelFileName = cs.useCSharpNamePolicy().getName(modelName, "class"); const subNsParts = getSubNamespaceParts(model.namespace, props.serviceNamespace); const modelContent = ; @@ -75,7 +76,7 @@ export function Models(props: ModelsProps): Children { ); return ( - + {wrappedContent} ); diff --git a/packages/http-server-csharp/src/components/render-root.tsx b/packages/http-server-csharp/src/components/render-root.tsx index e2405ba25a2..36e6b753180 100644 --- a/packages/http-server-csharp/src/components/render-root.tsx +++ b/packages/http-server-csharp/src/components/render-root.tsx @@ -26,11 +26,12 @@ export function ControllersAndInterfaces(props: ControllersAndInterfacesProps): const interfaceOps = props.interfaces.map((iface) => ({ iface, ops: props.canonicalOpsMap.get(iface.name) ?? [], + name: namePolicy.getName(iface.name, "class"), })); // Collect operations that need request model classes const requestModels: RequestModelInfo[] = []; - for (const { iface, ops } of interfaceOps) { + for (const { iface, ops, name } of interfaceOps) { for (const op of ops) { // GET requests don't have body parameters in the server if (op.method === "get") continue; @@ -45,7 +46,7 @@ export function ControllersAndInterfaces(props: ControllersAndInterfacesProps): const hasExplicitBody = body.bodies[0].property !== undefined; if (!hasExplicitBody) { const opName = namePolicy.getName(op.name, "class-method"); - const requestModelName = `${iface.name}${opName}Request`; + const requestModelName = `${name}${opName}Request`; requestModels.push({ name: requestModelName, op, ifaceName: iface.name }); } } @@ -60,13 +61,13 @@ export function ControllersAndInterfaces(props: ControllersAndInterfacesProps): - {({ iface, ops }) => { + {({ iface, ops, name }) => { const hasMultipart = ops.some( (op) => op.requestParameters.body?.bodyKind === "multipart", ); return ( - {({ iface, ops }) => { + {({ iface, ops, name }) => { const hasMultipart = ops.some( (op) => op.requestParameters.body?.bodyKind === "multipart", ); return ( { expect(launchSettings).toContain("https://localhost:7000;http://localhost:5000"); expect(launchSettings).toContain("http://localhost:5000"); }); + +it("uses C# type names for generated type files", async () => { + const [result] = await compileAndDiagnose( + tester, + getStandardService(` + enum camelEnum { + value + } + + model camelModel {} + + @route("/items") + interface camelInterface { + @post op create(item: string): void; + } + `), + { "emit-mocks": "mocks-and-project-files", "skip-format": true }, + ); + const files = [...result.fs.fs.keys()]; + + expect(files.some((path) => path.endsWith("/generated/models/CamelEnum.cs"))).toBe(true); + expect(files.some((path) => path.endsWith("/generated/models/CamelModel.cs"))).toBe(true); + expect( + files.some((path) => path.endsWith("/generated/models/CamelInterfaceCreateRequest.cs")), + ).toBe(true); + expect(files.some((path) => path.endsWith("/generated/operations/ICamelInterface.cs"))).toBe( + true, + ); + expect( + files.some((path) => path.endsWith("/generated/controllers/CamelInterfaceController.cs")), + ).toBe(true); + expect(files.some((path) => path.endsWith("/mocks/CamelInterface.cs"))).toBe(true); + expect(files.some((path) => path.includes("/generated/models/camel"))).toBe(false); + // cspell:ignore Icamel + expect(files.some((path) => path.includes("/generated/operations/Icamel"))).toBe(false); + expect(files.some((path) => path.includes("/generated/controllers/camel"))).toBe(false); +});