Goal
Enable PublishTrimmed=true for Cli.Embedded to reduce the standalone binary size.
This is Phase 2 of #40 — the static-link build is done, but actual trimming was never enabled.
Baseline
- Current Cli.Embedded single-file size: 46.5 MB (Brotli compressed)
- Expected after trimming: 25-35 MB (~30-40% reduction)
Investigation Results
A test build with PublishTrimmed=true + TrimMode=partial revealed 45 trim issues — all in our own code:
| Category |
Count |
API |
JsonSerializer without source-gen |
27 |
IL2026 |
| LINQ Expression building |
8 |
IL2026 (Expression.Property) |
MakeGenericMethod |
1 |
IL2060 |
Good news: Scriban, NetVips, Spectre.Console, System.CommandLine, Microsoft.Extensions.* are all trim-compatible. No third-party blockers.
Tasks
Phase 1: Foundation
Phase 2: JSON Source Generators (27 occurrences)
Replace untyped JsonSerializer.Serialize/Deserialize with [JsonSerializable] source-gen contexts.
Already done (5 contexts): PackageIndex, ThemeJsonConfig, NuGetSearch, Statistics, OneDrive, Calendar.
Remaining:
Phase 3: LINQ Expression Builder (8 occurrences)
Phase 4: MakeGenericMethod (1 occurrence)
Phase 5: Activate Trimming
Phase 6: Pipeline
References
Strategy
Work in small, testable steps — each phase should land as a separate PR. Do NOT enable PublishTrimmed until all analyzer issues are resolved, otherwise we trade compile-time errors for runtime crashes.
Goal
Enable
PublishTrimmed=trueforCli.Embeddedto reduce the standalone binary size.This is Phase 2 of #40 — the static-link build is done, but actual trimming was never enabled.
Baseline
Investigation Results
A test build with
PublishTrimmed=true+TrimMode=partialrevealed 45 trim issues — all in our own code:JsonSerializerwithout source-genIL2026IL2026(Expression.Property)MakeGenericMethodIL2060Good news: Scriban, NetVips, Spectre.Console, System.CommandLine, Microsoft.Extensions.* are all trim-compatible. No third-party blockers.
Tasks
Phase 1: Foundation
EnableTrimAnalyzer=truetoCore,Features/Generate,Features/Packages(will surface remaining issues)Phase 2: JSON Source Generators (27 occurrences)
Replace untyped
JsonSerializer.Serialize/Deserializewith[JsonSerializable]source-gen contexts.Already done (5 contexts):
PackageIndex,ThemeJsonConfig,NuGetSearch,Statistics,OneDrive,Calendar.Remaining:
Core/Services/GlobalConfigManager(Load + Save)Core/Themes/LocalThemeProviderFeatures/Generate/Services/ManifestService(Load + Save)Features/Generate/Services/RenderService.ResolveSingleDataSourceAsyncFeatures/Generate/Commands/ConfigImageCommand.LoadThemeDefaultsPhase 3: LINQ Expression Builder (8 occurrences)
Features/Generate/Filtering/FilterExpressionBuilder—Expression.Property(expr, name)is fundamentally trim-unsafe (uses reflection at runtime)[DynamicallyAccessedMembers], restructure to use typed accessors, or replace with source-generated filter compilerPhase 4: MakeGenericMethod (1 occurrence)
Features/Generate/Commands/CreatePageCommand.ExtractValues— annotate withDynamicallyAccessedMembersAttributeor restructurePhase 5: Activate Trimming
PublishTrimmed=true+TrimMode=partialinCli.Embedded.csprojPhase 6: Pipeline
PublishTrimmedto release.yml Standalone publish stepPublishTrimmedto ci.yml smoke-test (catches regressions early)References
IsTrimmablefoundation in Plugins/SDK)Strategy
Work in small, testable steps — each phase should land as a separate PR. Do NOT enable
PublishTrimmeduntil all analyzer issues are resolved, otherwise we trade compile-time errors for runtime crashes.