Skip to content

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

Open
lewing wants to merge 5 commits intodotnet:mainfrom
lewing:fix-wasm-framework-sourcetype
Open

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

Conversation

@lewing
Copy link
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
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
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
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
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
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

@lewing
Copy link
Member Author

lewing commented Mar 17, 2026

fix finally made it here

@lewing lewing enabled auto-merge (squash) March 17, 2026 18:40
Copilot AI review requested due to automatic review settings March 17, 2026 22:16
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 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.

@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from c17844c to 4140b97 Compare March 17, 2026 23:09
Copilot AI review requested due to automatic review settings March 17, 2026 23:18
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 4140b97 to 2eda97d Compare March 17, 2026 23:18
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 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.

@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 2eda97d to bac8c95 Compare March 17, 2026 23:29
Copilot AI review requested due to automatic review settings March 18, 2026 00:31
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from bac8c95 to 687f428 Compare March 18, 2026 00:31
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 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.

- 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>
@lewing lewing force-pushed the fix-wasm-framework-sourcetype branch from 687f428 to fa33973 Compare March 18, 2026 01:07
@lewing
Copy link
Member Author

lewing commented Mar 18, 2026

@akoeplinger how is the bootstrap sdk supposed to update now?

@akoeplinger
Copy link
Member

@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.

Copilot AI review requested due to automatic review settings March 19, 2026 20:49
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 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines +33 to +35
/// 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.
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
/// 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.

Copilot uses AI. Check for mistakes.
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.

5 participants