Skip to content

Use Framework SourceType for WASM pass-through assets in multi-client solutions#125309

Closed
lewing wants to merge 1 commit intodotnet:mainfrom
lewing:fix-wasm-duplicate-identity
Closed

Use Framework SourceType for WASM pass-through assets in multi-client solutions#125309
lewing wants to merge 1 commit intodotnet:mainfrom
lewing:fix-wasm-duplicate-identity

Conversation

@lewing
Copy link
Member

@lewing lewing commented Mar 8, 2026

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 DiscoverPrecompressedAssets with an ArgumentException on ToDictionary.

Root Cause

PR #124125 correctly changed WASM ContentRoot from project-specific OutputPath copies 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 like dotnet.js.map now resolve to identical NuGet cache paths, producing duplicate Identity values.

Fix

Instead of copying pass-throughs to the intermediate output path (which risks re-introducing the staleness bug #124125 fixed), materialize them to a per-project obj/fx/{PackageId}/ directory using the Framework SourceType convention from the SWA SDK (dotnet/sdk#53135).

  • Pass-through files (JS, maps, ICU, native wasm, and DLLs when WebCil is disabled): copied to obj/fx/{PackageId}/, registered with SourceType="Framework"
  • WebCil-converted files: remain in obj/webcil/, registered with SourceType="Computed" (already per-project)
  • Satellite assemblies: placed in culture subdirectories in both cases
  • Both groups get per-item ContentRoot for correct Identity resolution

Architecture

This follows the SWA Framework materialization pattern: framework assets are "adopted" by each consuming project, giving them unique per-project Identity values while properly modeling the ownership relationship. AssetMode=CurrentProject prevents assets from leaking to references.

Dependencies

Requires SDK support for SourceType="Framework" from dotnet/sdk#53135.

Testing

Validated with:

  • Multi-client build (2 WASM clients): ✅ 221 framework assets materialized per project
  • Multi-client publish: ✅ Both clients produce 141 fingerprinted files
  • Incremental rebuild: ✅
  • WebCil disabled (WasmEnableWebcil=false): ✅ All files as Framework pass-throughs
  • aspnetcore Components.TestServer (3 WASM clients, WasmMinimal + WasmRemoteAuthentication + BasicTestApp): ✅ Build succeeded, 0 errors

Related

@lewing lewing requested a review from akoeplinger as a code owner March 8, 2026 16:30
Copilot AI review requested due to automatic review settings March 8, 2026 16:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a build-time crash in multi-Blazor-WASM-client solutions caused by duplicate StaticWebAsset Identity values when multiple projects reference the same runtime pack files from the NuGet cache. It does so by copying “pass-through” runtime assets into a per-project intermediate directory so each project’s asset Identity resolves to a unique on-disk path.

Changes:

  • Identify WebCil candidates that aren’t already produced under the project’s $(IntermediateOutputPath)webcil/ folder.
  • Copy those pass-through files into $(_WasmBuildWebcilPath) to ensure per-project uniqueness.
  • Re-add the copied items as WebCil candidates and track the copied outputs in FileWrites.

@lewing lewing marked this pull request as draft March 8, 2026 16:56
@lewing lewing force-pushed the fix-wasm-duplicate-identity branch from b36f57f to 965b95e Compare March 8, 2026 17:33
Copilot AI review requested due to automatic review settings March 8, 2026 18:07
@lewing lewing force-pushed the fix-wasm-duplicate-identity branch from 965b95e to 6c3122c Compare March 8, 2026 18:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

@lewing
Copy link
Member Author

lewing commented Mar 9, 2026

After further analysis and feedback, the SDK-only approach is the better fix. Created dotnet/sdk#53328 which handles duplicate Identity in ToAssetDictionary, DiscoverPrecompressedAssets, GenerateStaticWebAssetsManifest, and GenerateStaticWebAssetEndpointsManifest.

The copy-based approach in this PR re-introduces the staleness problem that #124125 was fixing — if the runtime pack changes on incremental build and ConvertDllsToWebcil doesn't re-run, copies in obj/ go stale. The SDK-only fix avoids this entirely.

Planning to close this PR in favor of the SDK fix.

maraf
maraf previously approved these changes Mar 9, 2026
@maraf maraf self-requested a review March 9, 2026 12:14
@maraf maraf dismissed their stale review March 9, 2026 12:16

Stale

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copying locally is the right approach for the time being. We shouldn't include multiple conflicting definitions of the same asset in the pipeline.

… solutions

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
DiscoverPrecompressedAssets with an ArgumentException.

Instead of copying pass-throughs to the intermediate output path (which risks staleness
on incremental builds when the runtime pack changes), materialize them to a per-project
obj/fx/{PackageId}/ directory using the Framework SourceType convention from the SWA SDK.
This gives each project a unique Identity while properly modeling the relationship:
these are framework assets adopted by each consuming project.

- Pass-through files: copied to obj/fx/{PackageId}/, registered with SourceType=Framework
- WebCil-converted files: remain in obj/webcil/, registered with SourceType=Computed
- Satellite assemblies: placed in culture subdirectories in both cases
- Both groups get per-item ContentRoot for correct Identity resolution

Requires SDK support for SourceType=Framework (dotnet/sdk#53135).

Fixes duplicate-key crash introduced by dotnet#124125.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lewing lewing force-pushed the fix-wasm-duplicate-identity branch from 6c3122c to 31070bb Compare March 9, 2026 13:13
@lewing lewing changed the title Fix duplicate StaticWebAsset Identity crash for multi-WASM-client solutions Use Framework SourceType for WASM pass-through assets in multi-client solutions Mar 9, 2026
@lewing
Copy link
Member Author

lewing commented Mar 9, 2026

Updated to use Javier's preferred approach: Framework SourceType (from dotnet/sdk#53135) instead of the copy-to-intermediate pattern.

What changed

Pass-through files are now materialized to obj/fx/{PackageId}/ and registered with SourceType="Framework" via a second DefineStaticWebAssets call, while webcil-converted files remain SourceType="Computed". This follows the SWA Framework convention where framework assets are adopted by each consuming project with unique per-project Identity values.

Why this is better than the previous approach

  1. No staleness risk — if the runtime pack changes, the framework materialization path stays consistent and SkipUnchangedFiles handles incrementality correctly
  2. Architecturally correct — uses the same Framework SourceType pattern that the SWA SDK uses for other framework assets
  3. No duplicate Identity — each project materializes to its own obj/fx/{PackageId}/ directory

Validation

  • Multi-client build/publish with 2 WASM clients ✅
  • aspnetcore Components.TestServer with 3 WASM clients ✅
  • WebCil disabled mode ✅
  • Incremental rebuild ✅

Dependency

This PR requires dotnet/sdk#53135 to be merged first (adds Framework SourceType support to the SDK).

@lewing
Copy link
Member Author

lewing commented Mar 9, 2026

Closing in favor of #125329 which is a clean PR for the Framework SourceType approach. This PR's branch was force-pushed over and no longer has the original copy-based approach history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants