Skip to content

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

Merged
lewing merged 10 commits intodotnet:mainfrom
lewing:fix-wasm-framework-sourcetype
Mar 25, 2026
Merged

Use Framework SourceType for WASM pass-through assets in multi-client solutions#125329
lewing merged 10 commits intodotnet:mainfrom
lewing:fix-wasm-framework-sourcetype

Conversation

@lewing
Copy link
Copy Markdown
Member

@lewing lewing commented Mar 9, 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

Use the SWA Framework SourceType convention (dotnet/sdk#53135) to let the SDK handle materialization, rather than manual Copy tasks:

  1. DefineStaticWebAssets registers pass-through files with SourceType="Framework" (using their NuGet cache paths)
  2. UpdatePackageStaticWebAssets materializes them to per-project obj/fx/{SourceId}/ directories, transforming metadata:
    • SourceTypeDiscovered
    • SourceId → project PackageId
    • BasePath → project StaticWebAssetBasePath
    • AssetModeCurrentProject
    • Identity → materialized file path (unique per project)

Asset classification:

  • Pass-through files (JS, maps, ICU, native wasm, DLLs when WebCil disabled): Framework → materialized by SDK task
  • WebCil-converted files: SourceType="Computed" (already per-project in obj/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:

  • aspnetcore Components.TestServer (3 WASM clients): ✅ Build succeeded
    • WasmMinimal: 22 framework + 205 webcil files
    • WasmRemoteAuthentication: 20 framework + 204 webcil files
    • BasicTestApp: 29 framework + 218 webcil files
  • Multi-client build (2 WASM clients): ✅ Framework assets materialized per project
  • Multi-client publish: ✅ Both clients produce fingerprinted files
  • Incremental rebuild: ✅ Timestamp-based skip
  • WebCil disabled (WasmEnableWebcil=false): ✅

Related

Copy link
Copy Markdown
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 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 _WasmFrameworkAssetPath property (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 with SourceType="Framework" via a new DefineStaticWebAssets call
  • Satellite (culture) framework assets are handled with culture subdirectories, matching the existing ConvertDllsToWebcil behavior

… 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>
@lewing
Copy link
Copy Markdown
Member Author

lewing commented Mar 9, 2026

This will fail until the sdk with the Framework source type makes it to runtime

@lewing
Copy link
Copy Markdown
Member Author

lewing commented Mar 13, 2026

waiting on #125419 now

Copilot AI review requested due to automatic review settings March 14, 2026 15:15
@lewing lewing marked this pull request as ready for review March 14, 2026 15:15
@lewing lewing requested a review from akoeplinger as a code owner March 14, 2026 15:15
Copy link
Copy Markdown
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 no new comments.


You can also share your feedback on Copilot code review. Take the survey.

@lewing
Copy link
Copy Markdown
Member Author

lewing commented Mar 14, 2026

@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

Copilot AI review requested due to automatic review settings March 24, 2026 17:04
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from c45416b to 95cf3c0 Compare March 24, 2026 17:04
Copy link
Copy Markdown
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 2 out of 2 changed files in this pull request and generated 1 comment.

@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 95cf3c0 to a78c28f Compare March 24, 2026 18:47
Copy link
Copy Markdown
Member Author

@lewing lewing left a comment

Choose a reason for hiding this comment

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

Addressing review feedback:

Comment accuracy (copilot-reviewer, line 419): Fixed — updated comment to accurately describe what UpdatePackageStaticWebAssets changes (SourceType, AssetMode, Identity/path remapping) without claiming SourceId→PackageId since both are already $(PackageId).

IsEnabled=false DLL handling (copilot-reviewer, line 67): Good point about the multi-client WasmEnableWebcil=false scenario. Currently when webcil is disabled, DLLs go through _WebcilAssetsCandidates as Computed with per-item ContentRoot, so Identity = their real NuGet cache path. In multi-client, this means two clients would get identical Identity for the same DLL — the original duplicate-Identity bug. However, this is the pre-existing behavior (before this PR, DLLs also went through the Computed path as-is). Fixing this properly requires routing shared DLLs through Framework materialization when IsEnabled=false, but the build targets Remove logic relies on DLLs being in WebcilCandidates for the publish path. I'll track this as a follow-up — the multi-client + NoWebcil combination is rare and the current PR doesn't regress it.

Output path clarification (@maraf, line 434): Updated comment to clarify — the materialized path is the intermediate one (obj/fx/{SourceId}/), and CopyToOutputDirectory=PreserveNewest causes the SDK to copy from there to bin/wwwroot/_framework/ at build time. UpdatePackageStaticWebAssets defaults CopyToOutputDirectory to Never, so without this override the framework files (dotnet.native.js, ICU data, etc.) don't make it to bin.

…ToOutputDirectory

Two fixes:

1. ConvertDllsToWebcil: When IsEnabled=false, apply the same filtering
   for PassThroughCandidates as the enabled path — only non-DLL items
   without WasmNativeBuildOutput metadata qualify as Framework
   pass-throughs. Previously all candidates (including DLLs) were
   marked as pass-throughs, causing them all to go through Framework
   materialization which triggered NETSDK1147 workload detection in
   NoWebcil/NoWorkload test configurations.

2. Browser.targets: Set CopyToOutputDirectory=PreserveNewest on
   materialized framework assets after UpdatePackageStaticWebAssets.
   The SDK task defaults materialized assets to CopyToOutputDirectory=
   Never, but WASM framework files (dotnet.native.js, ICU data, etc.)
   must be copied to bin/wwwroot/_framework for the app to work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 24, 2026 18:50
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from a78c28f to d92ebd8 Compare March 24, 2026 18:50
Copy link
Copy Markdown
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 2 out of 2 changed files in this pull request and generated 1 comment.

@lewing lewing requested a review from ilonatommy as a code owner March 24, 2026 19:08
Copilot AI review requested due to automatic review settings March 24, 2026 19:16
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 712476d to cf505eb Compare March 24, 2026 19:16
Copy link
Copy Markdown
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 3 out of 3 changed files in this pull request and generated no new comments.

@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from cf505eb to 62ab1d4 Compare March 24, 2026 19:23
Validates that two Blazor WASM client projects can be built by a single
server project without duplicate static web asset Identity collisions.
This is the scenario that the Framework SourceType + UpdatePackageStaticWebAssets
materialization path was designed to fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 24, 2026 19:31
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 62ab1d4 to 7847a96 Compare March 24, 2026 19:31
Copy link
Copy Markdown
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 3 out of 3 changed files in this pull request and generated 1 comment.

@lewing lewing merged commit 210efc0 into dotnet:main Mar 25, 2026
43 checks passed
@lewing lewing deleted the fix-wasm-framework-sourcetype branch March 25, 2026 01:01
lewing added a commit to lewing/runtime that referenced this pull request Mar 27, 2026
Add a publish variant of the existing MultiClientHostedBuild test to
validate that multi-client hosted Blazor WASM scenarios work correctly
during publish, not just build. The test creates two WASM clients with
different StaticWebAssetBasePath values hosted by a single server project
and verifies dotnet publish succeeds without duplicate Identity crashes.

This complements the build-time test added in dotnet#125329 and ensures the
Framework SourceType materialization path produces per-project Identity
values that survive through the publish pipeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Mar 27, 2026
Add a publish variant of the existing MultiClientHostedBuild test to
validate that multi-client hosted Blazor WASM scenarios work correctly
during publish, not just build. The test creates two WASM clients with
different StaticWebAssetBasePath values hosted by a single server project
and verifies dotnet publish succeeds without duplicate Identity crashes.

This complements the build-time test added in dotnet#125329 and ensures the
Framework SourceType materialization path produces per-project Identity
values that survive through the publish pipeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

6 participants