diff --git a/src/Components/AGENTS.md b/src/Components/AGENTS.md index fd579b2a3447..46d71464dc9e 100644 --- a/src/Components/AGENTS.md +++ b/src/Components/AGENTS.md @@ -66,6 +66,64 @@ Together these cover every interactivity platform (Server/WebAssembly/Auto/None) 6. **Clean up sample code** - After your E2E tests are passing, remove the sample code you added to the Samples projects. The sample was only for development and interactive testing; the E2E tests now provide the permanent test coverage. Use `git checkout -- src/Components/Samples` and `git clean -df -- src/Components/Samples` to remove the sample code. +## Design invariants + +Apply these rules to framework code in this area. Before flagging code, check the current +analyzer, host-assembly boundary, and nearby pattern. Examples are anchors, not exhaustive +inventories. + +### Render modes + +- Framework components must be render-mode agnostic so they work when consumed from a Razor + Class Library. The app chooses the render mode; framework components do not force one with + `@rendermode`. +- Host-specific APIs and components may live in concrete host assemblies, but that set is + deliberately small - adding to it needs a strong reason. Anything intended for broad app or + library consumption needs a render-mode-agnostic abstraction. + +### `HttpContext` and host services + +- Do not use `IHttpContextAccessor` in framework components or services. Components receive + `HttpContext` as a cascading parameter: + + ```csharp + [CascadingParameter] public HttpContext? HttpContext { get; set; } + ``` + +- Services accept `HttpContext` as a method parameter, never as a constructor dependency. +- Features consumed by class libraries use a render-mode-agnostic abstraction with an + implementation and service registration for each supported host. Antiforgery is the + reference pattern. + +### Rendering and event dispatch + +- Analyzer `BL0012` is authoritative for redundant `StateHasChanged()` calls in + `OnInitialized[Async]`, `OnParametersSet[Async]`, and event-callback handlers. Calls from + after-render logic, helper methods, or external callbacks may be required; check the + analyzer's current scope before flagging them. +- Do not use `InvokeAsync` to re-enter a synchronization context that framework code is already + on. Use it to marshal external callbacks onto the renderer's context. + This rule covers `ComponentBase.InvokeAsync` only: `Dispatcher.InvokeAsync` in hosting + infrastructure is the intended dispatch point, and `EventCallback.InvokeAsync` is an + unrelated callback-invocation API. +- Do not implement `IHandleEvent` in new components. `ComponentBase` already provides automatic + rendering after event handlers. + +### Trimming and dependencies + +- Handle reflection with trim/AOT annotations such as `[DynamicallyAccessedMembers]`, + `[RequiresUnreferencedCode]`, and `[RequiresDynamicCode]`. Do not impose a blanket reflection + ban or source-generated-serialization-only rule; JS interop itself uses annotated reflection. +- Do not add package dependencies to shipping Components projects by default. Any addition + needs explicit justification against the nearby project pattern. + +### Coordinating async work in tests + +Prefer `TaskCompletionSource` to coordinate async work. Do not use `Task.Delay` or +`Thread.Sleep` to wait for state changes. Legitimate exceptions include real-browser polling, +duration assertions, analyzer fixture source text that is the input being analyzed, and +deliberate slow-work simulation. + ## Build Tips ### Efficient Build Strategy @@ -224,7 +282,9 @@ E2E tests are located in `src/Components/test/E2ETest`. ### Running E2E Tests -The E2E tests use Selenium. To build and run tests: +The E2E tests use Selenium. Playwright references in this guide mean the Playwright MCP browser +tool for manual interactive validation, not the E2E framework. Adding another E2E framework +requires agreement from Components maintainers. To build and run tests: ```bash # Build the E2E test project and its dependencies