Use Framework SourceType for WASM pass-through assets in multi-client solutions#125329
Use Framework SourceType for WASM pass-through assets in multi-client solutions#125329lewing wants to merge 5 commits intodotnet:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a crash introduced by PR #124125 in multi-client Blazor WebAssembly solutions. When two WASM client projects reference the same runtime pack, pass-through assets (JS, maps, ICU data, native WASM) previously resolved to identical NuGet cache paths, causing duplicate Identity keys that crashed DiscoverPrecompressedAssets with an ArgumentException during ToDictionary. The fix materializes pass-through files to a per-project obj/fx/{PackageId}/ directory using the SourceType="Framework" convention from the SWA SDK (requires dotnet/sdk#53135).
Changes:
- New
_WasmFrameworkAssetPathproperty (obj/fx/{PackageId}/) for materializing pass-through assets - Pass-through files are partitioned from WebCil-converted files and copied to the per-project
obj/fx/{PackageId}/path, registered withSourceType="Framework"via a newDefineStaticWebAssetscall - Satellite (culture) framework assets are handled with culture subdirectories, matching the existing
ConvertDllsToWebcilbehavior
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
… solutions
Multi-WASM-client solutions crash with duplicate Identity keys in
DiscoverPrecompressedAssets because pass-through files (JS, maps, ICU,
native wasm) share the same NuGet cache path across projects.
Instead of manually copying pass-throughs, use the SWA Framework
SourceType convention: DefineStaticWebAssets registers them as
SourceType="Framework", then UpdatePackageStaticWebAssets materializes
them to per-project obj/fx/{SourceId}/ directories, transforming
metadata (SourceType→Discovered, SourceId→PackageId,
AssetMode→CurrentProject) and giving each project a unique Identity.
This approach:
- Eliminates manual Copy tasks from Browser.targets
- Leverages the SDK's MaterializeFrameworkAsset for timestamp-based
incremental copies
- Properly models the relationship: framework assets adopted by each
consuming project
- Works with and without WebCil enabled
Depends on dotnet/sdk#53135 for Framework SourceType support in the SDK.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9b1f0a8 to
1c6e5b6
Compare
|
This will fail until the sdk with the Framework source type makes it to runtime |
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
|
waiting on #125419 now |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
You can also share your feedback on Copilot code review. Take the survey.
|
@javiercn I know you have some follow-up planed to make this cleaner but it we need to get preview3 green asap, this is blocking dotnet/aspnetcore#65673 |
|
fix finally made it here |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
You can also share your feedback on Copilot code review. Take the survey.
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Outdated
Show resolved
Hide resolved
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Show resolved
Hide resolved
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Show resolved
Hide resolved
c17844c to
4140b97
Compare
4140b97 to
2eda97d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
You can also share your feedback on Copilot code review. Take the survey.
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Outdated
Show resolved
Hide resolved
2eda97d to
bac8c95
Compare
bac8c95 to
687f428
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
You can also share your feedback on Copilot code review. Take the survey.
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Show resolved
Hide resolved
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Outdated
Show resolved
Hide resolved
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Outdated
Show resolved
Hide resolved
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Show resolved
Hide resolved
- Add PassThroughCandidates output to ConvertDllsToWebcil task so the task itself classifies which files are pass-throughs (non-DLL files from shared locations like the NuGet cache) vs converted webcil files. This replaces the fragile MSBuild string-comparison split that checked StartsWith(_WasmBuildWebcilPath) and incorrectly classified native build outputs in obj/wasm/for-build/ as Framework candidates. - Set AssetMode=All on materialized framework assets so they flow through project references to the host/server project in Blazor WASM hosted scenarios. UpdatePackageStaticWebAssets defaults to CurrentProject which prevents the server project from seeing client framework assets during publish. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
687f428 to
fa33973
Compare
|
@akoeplinger how is the bootstrap sdk supposed to update now? |
|
@lewing the one in global.json you mean? normally the VMR/arcade sets the minimum version via rebootstrapping process in the VMR which then flows to repos via backflow. |
| /// When IsEnabled is false, all candidates (DLL and non-DLL) appear here. | ||
| /// Items with WasmNativeBuildOutput metadata (per-project native build outputs) | ||
| /// are excluded — they're already unique per project. |
There was a problem hiding this comment.
The XML doc for PassThroughCandidates says items with WasmNativeBuildOutput metadata are excluded, but Execute() assigns PassThroughCandidates = Candidates when IsEnabled is false, which will include those items. Please update the doc (or adjust the disabled-path behavior) so the exclusion rule matches what actually happens when WebCil is disabled.
| /// When IsEnabled is false, all candidates (DLL and non-DLL) appear here. | |
| /// Items with WasmNativeBuildOutput metadata (per-project native build outputs) | |
| /// are excluded — they're already unique per project. | |
| /// When <see cref="IsEnabled"/> is true, items with WasmNativeBuildOutput metadata | |
| /// (per-project native build outputs) are excluded — they're already unique per project. | |
| /// When <see cref="IsEnabled"/> is false, all candidates (DLL and non-DLL), including | |
| /// those with WasmNativeBuildOutput metadata, appear here with no additional filtering. |
Summary
When multiple Blazor WebAssembly client projects reference the same runtime pack, pass-through files (JS, maps, ICU data, native wasm) share a NuGet cache path. This causes duplicate Identity keys in the static web assets pipeline, crashing
DiscoverPrecompressedAssetswith anArgumentExceptiononToDictionary.Root Cause
PR #124125 correctly changed WASM
ContentRootfrom project-specificOutputPathcopies to per-item%(RootDir)%(Directory)(pointing to the real file in the NuGet cache). This fixed staleness on incremental builds. However, when two WASM client projects reference the same runtime pack, shared files likedotnet.js.mapnow resolve to identical NuGet cache paths, producing duplicate Identity values.Fix
Use the SWA Framework SourceType convention (dotnet/sdk#53135) to let the SDK handle materialization, rather than manual Copy tasks:
DefineStaticWebAssetsregisters pass-through files withSourceType="Framework"(using their NuGet cache paths)UpdatePackageStaticWebAssetsmaterializes them to per-projectobj/fx/{SourceId}/directories, transforming metadata:SourceType→DiscoveredSourceId→ projectPackageIdBasePath→ projectStaticWebAssetBasePathAssetMode→CurrentProjectIdentity→ materialized file path (unique per project)Asset classification:
SourceType="Computed"(already per-project inobj/webcil/)This eliminates manual Copy/culture-handling logic from Browser.targets and properly models the relationship: framework assets adopted by each consuming project.
Dependencies
Requires SDK support for
SourceType="Framework"from dotnet/sdk#53135.Testing
Validated with patched SDK (Framework SourceType + MaterializeFrameworkAsset) + Browser.targets:
WasmEnableWebcil=false): ✅Related