Dispatch declaration overrides in Experimental_ComponentOverrides - #11597
Conversation
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
e806960 to
bbf0c6c
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends Experimental_ComponentOverrides so emitters can override declarations (not just references), and wires the C# declaration components (ClassDeclaration, Property, EnumDeclaration) through the overridable dispatch point. This unblocks customizing how types are declared without forking framework components, supporting the ongoing migration of @typespec/http-server-csharp onto the emitter framework.
Changes:
- Add declaration-override typing + dispatch support to
Experimental_OverridableComponentand override config generics. - Wrap C#
ClassDeclaration,Property, andEnumDeclarationrender paths withExperimental_OverridableComponentindeclarationmode. - Add C# tests covering declaration override behavior and add a Chronus changelog entry.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/emitter-framework/src/csharp/components/property/property.tsx | Routes property declarations through the overridable declaration dispatch. |
| packages/emitter-framework/src/csharp/components/enum/declaration.tsx | Routes enum declarations through the overridable declaration dispatch. |
| packages/emitter-framework/src/csharp/components/class/declaration.tsx | Routes class declarations through the overridable declaration dispatch. |
| packages/emitter-framework/src/csharp/components/class/declaration.test.tsx | Adds tests validating declaration overrides for class/property/enum declarations. |
| packages/emitter-framework/src/core/components/overrides/config.ts | Extends overrides config APIs to carry a generic declaration-props type parameter. |
| packages/emitter-framework/src/core/components/overrides/component-overrides.tsx | Adds declaration override types and dispatch logic to Experimental_OverridableComponent. |
| .chronus/changes/ef-declaration-overrides-2026-8-5.md | Changelog entry for declaration override support. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…al_ComponentOverrides The `declaration` descriptor existed but nothing dispatched to it, so an emitter could override how a type is referenced but not how it is declared. The C# `ClassDeclaration`, `Property` and `EnumDeclaration` now render through the override point.
bbf0c6c to
b10f5fb
Compare
The emitter carried a 217-line hand-maintained table of C# keywords and a `createLibrary` call re-declaring the `System.Text.Json.Serialization` attributes. `@alloy-js/csharp` ships both, and its versions are better: the local keyword list was missing `delegate`, and hand-declared symbols do not participate in Alloy's automatic `using` management. Both are deleted in favour of the Alloy equivalents (`isValidCSharpIdentifier`, `csharpKeywords`, the `System/Text/Json` builtins), along with the local `getDocComments` copy, which is already exported from `@typespec/emitter-framework/csharp`. One thing Alloy deliberately does *not* do is worth calling out: its name policy `@`-escapes real keywords, but the emitter needs namespace segments colliding with common BCL type names to be **renamed** — a namespace called `Type` shadows `System.Type` and breaks every `typeof()` in the generated converters. That rule survives, now isolated in `getCSharpNamespaceName` and built on Alloy's keyword sets rather than a parallel list. Generated output is unchanged. --- Independent — targets `main` and can merge on its own. One of 7 PRs moving `@typespec/http-server-csharp` onto the emitter framework instead of its private forks: microsoft#11596, microsoft#11597, microsoft#11598, microsoft#11599, microsoft#11600, microsoft#11601, microsoft#11602. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The C# `TypeExpression` threw a hard `Error` for anything it did not recognise — tuples, string templates, enum members, template parameters and most of the intrinsics. An emitter hitting one of those got a crash with no source location instead of a diagnostic, so `@typespec/http-server-csharp` had to wrap every call site in `try`/`catch`. Those kinds are now handled, and a genuinely unsupported type reports a diagnostic against the offending type and falls back to `object`, so compilation continues and the user gets a pointer to the spec that caused it. The diagnostics name the type, so it is actionable without bisecting the spec: ``` warning emitter-framework/csharp-unsupported-scalar: Scalar 'MyLib.ipAddress' has no C# equivalent, using 'object' instead. Extend a built-in scalar to control how it is emitted. warning emitter-framework/csharp-unsupported-type: Type '@MyLib.flag' of kind 'Decorator' is not supported in C#, using 'object' instead. ``` The two are kept separate because they call for different actions: the scalar one is fixable in the spec by extending a built-in scalar, while the other reports a gap in the emitter. Two smaller bugs fixed along the way: - Unsupported scalars reported a **TypeScript** diagnostic (`typescript-unsupported-scalar`) from the C# components. C# now has its own `src/csharp/lib.ts`, matching the existing `typescript/lib.ts` and `python/lib.ts`, which makes that class of mistake a type error. - The `null` and `never` intrinsics emitted `null` and `never`, neither of which is a C# type. They now emit `object` and `void`. Also adds an `isCSharpValueType` util, since deciding whether a TypeSpec type maps to a C# struct is something every C# emitter needs and every C# emitter was reimplementing. --- Independent — targets `main` and can merge on its own. One of 7 PRs moving `@typespec/http-server-csharp` onto the emitter framework instead of its private forks: microsoft#11596, microsoft#11597, microsoft#11598, microsoft#11599, microsoft#11600, microsoft#11601, microsoft#11602.
An override descriptor already accepted a
declarationentry, but nothing ever dispatched to it — onlyreferencewas wired up. So an emitter could change how a type is referenced and had no say in how it is declared. The only way to customize a declaration was to copy the framework component into your own package and diverge from it, which is exactly what@typespec/http-server-csharpdid for classes, properties and enums.Experimental_OverridableComponentnow dispatchesdeclarationtoo, and the C#ClassDeclaration,PropertyandEnumDeclarationrender through it. An override can replace the declaration outright, or re-render the default with different props:props.Declarationis the unwrapped body, so re-rendering it does not recurse back through the override.The declaration props type is generic and defaults to
Record<string, any>, because the existingExperimental_CustomTypeToPropsmap is TypeScript-specific (VarDeclarationProps,ObjectPropertyProps) and means nothing for C#. Callers that want type safety pass the concrete props type:.forTypeKind<"ModelProperty", PropertyProps>(...).Independent — targets
mainand can merge on its own.One of 7 PRs moving
@typespec/http-server-csharponto the emitter framework instead of its private forks: #11596, #11597, #11598, #11599, #11600, #11601, #11602.