diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a6fee6f..890a4b5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,7 +20,9 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v6 with: - dotnet-version: 10.x + dotnet-version: | + 8.x + 10.x - name: Checkout uses: actions/checkout@v7 @@ -30,8 +32,16 @@ jobs: - name: Build run: dotnet build -c Release + - name: Build (AOT Analyze) + run: dotnet build src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj -c Release -f net8.0 --no-incremental --no-restore --warnaserror -p:EnableAotAnalysis=true + + # NOTE: net8.0 is CI-only (AOT analysis above); the package ships net6.0 assets only. + # Do not "fix" this to pack all TargetFrameworks without also updating the csproj comment. + - name: Restore NuGet Package + run: dotnet restore src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj -p:TargetFrameworks=net6.0 + - name: Create NuGet Packages - run: dotnet pack -c Release -o ./nuget --no-build + run: dotnet pack src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj -c Release -o ./nuget --no-build --no-restore -p:TargetFrameworks=net6.0 - name: NuGet login (OIDC) id: login diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67dc13a..fe62c92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,9 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v6 with: - dotnet-version: 10.x + dotnet-version: | + 8.x + 10.x - name: Checkout uses: actions/checkout@v7 @@ -26,6 +28,9 @@ jobs: - name: Build (Release) run: dotnet build -c Release + - name: Build (AOT Analyze) + run: dotnet build src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj -c Release -f net8.0 --no-incremental --no-restore --warnaserror -p:EnableAotAnalysis=true + - name: Test (Debug) run: dotnet test -c Debug --no-build diff --git a/README.md b/README.md index fd12ac5..6688cb4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ HtmxToolkit is designed to minimize HTMX integration overhead in the application In normal use, they incur no wrapper allocations while preserving a strongly typed API. - Version-specific HTMX configuration is serialized only when the configuration changes; the resulting JSON is cached and reused across requests. - Known JSON shapes use source-generated `System.Text.Json` metadata, avoiding reflection-based metadata discovery at runtime. - Event details passed to `TriggerEvent` are the deliberate exception because their types are defined by the application. + Applications can pass `JsonTypeInfo` to `TriggerEvent` to serialize their event details without reflection. - Work is skipped for non-HTMX requests, and overloads that accept state allow callers to use static callbacks and avoid closure allocations. ## Installation @@ -204,6 +204,14 @@ Response.Htmx( Request.Path.Value); ``` +For trimming and Native AOT, pass source-generated JSON metadata for the event detail: + +```csharp +Response.Htmx( + static (htmx, detail) => htmx.TriggerEvent("profile-updated", detail, AppJsonContext.Default.ProfileUpdated), + detail); +``` + Call `Response.GetHtmxHeaders()` for direct access to the strongly typed response headers, or use `HtmxResponseHeaderNames` with lower-level APIs. ### Declarative Responses diff --git a/docs/api-overwrites/responses.md b/docs/api-overwrites/responses.md index 03d8cbd..8e64cc9 100644 --- a/docs/api-overwrites/responses.md +++ b/docs/api-overwrites/responses.md @@ -40,6 +40,13 @@ example: [!code-csharp[](../snippets/responses/TriggerClientEvent.cs)] --- +--- +uid: Ramstack.HtmxToolkit.HtmxResponse.TriggerEvent``1(System.String,``0,System.Text.Json.Serialization.Metadata.JsonTypeInfo{``0},Ramstack.HtmxToolkit.HtmxTriggerTiming) +example: + - |- + [!code-csharp[](../snippets/responses/TriggerClientEventAot.cs)] +--- + --- uid: Ramstack.HtmxToolkit.HtmxResponseAttribute example: diff --git a/docs/articles/responses.md b/docs/articles/responses.md index 90ac330..5f7808a 100644 --- a/docs/articles/responses.md +++ b/docs/articles/responses.md @@ -124,6 +124,27 @@ Response.Htmx(htmx => htmx.TriggerEvent( HtmxTriggerTiming.AfterSwap)); ``` +The object overload uses reflection to serialize event details. For trimming and Native AOT, +pass source-generated JSON metadata instead: + +```csharp +var detail = new ProductSaved(product.Id); + +Response.Htmx( + static (htmx, detail) => htmx.TriggerEvent( + "product-saved", + detail, + AppJsonContext.Default.ProductSaved, + HtmxTriggerTiming.AfterSwap), + detail); + +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] +[JsonSerializable(typeof(ProductSaved))] +internal partial class AppJsonContext : JsonSerializerContext; + +internal sealed record ProductSaved(int Id); +``` + ```html