Skip to content

[release/10.0.3xx] Backport #52816#52881

Draft
lewing wants to merge 3 commits intorelease/10.0.3xxfrom
backport-52816-10.0.3xx
Draft

[release/10.0.3xx] Backport #52816#52881
lewing wants to merge 3 commits intorelease/10.0.3xxfrom
backport-52816-10.0.3xx

Conversation

@lewing
Copy link
Member

@lewing lewing commented Feb 6, 2026

Backport of #52816 to release/10.0.3xx, based on @maraf's PR #52710.

Customer Issue

When multiple Blazor WASM client projects (e.g., FirstClient and SecondClient) are hosted by a single server project, users encounter a duplicate key error in FilterStaticWebAssetEndpoints. Both clients define the same HotReload JS module (Microsoft.DotNet.HotReload.WebAssembly.Browser.lib.module.js) as a static web asset with identical Identity values, causing the build to fail.

Additionally, the HotReload assembly version could be unpredictable when pulled from NuGet package sources, leading to potential version conflicts between the SDK and the referenced package.

Users cannot easily workaround these issues without modifying their project structure to use a single Blazor WASM client.

Description

This PR fixes both issues by embedding the HotReload functionality directly in the WebAssembly SDK:

  1. Embeds HotReload in SDK: Replaces the PackageReference with content shipped inside the SDK, ensuring consistent versioning
  2. Copy to intermediate folder: The HotReload JS module is copied to hotreload\ before being defined as a StaticWebAsset
  3. Unique Identity: Each project now has a unique Identity for the asset (pointing to their own intermediate folder)
  4. No filter changes needed: The copy-to-intermediate pattern naturally avoids duplicates without modifying filter logic

This follows the established pattern used by JSModules, ScopedCss, and ServiceWorker targets, and @javiercn's guidance: "What we do in other situations with assets like this is to copy them to the intermediate output folder before defining them."

Was this a regression?

  • Yes
  • No

Testing

The Publish_HostingMultipleBlazorWebApps_Works test now passes and validates the fix.

Risk

Low - this follows established patterns used elsewhere in the SDK for similar assets (JSModules, ScopedCss, ServiceWorker), and the change is well-tested. Embedding in the SDK also eliminates version conflicts.

Co-authored-by: Marek Fišera <mara@neptuo.com>
@lewing lewing requested review from a team, akoeplinger and tmat as code owners February 6, 2026 19:25
Copilot AI review requested due to automatic review settings February 6, 2026 19:25
@lewing lewing requested review from a team, maraf and pavelsavara as code owners February 6, 2026 19:25
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

Backport to release/10.0.3xx of the WebAssembly Hot Reload embedding work to avoid duplicate static web asset identities when multiple Blazor WASM clients are hosted by a single server, and to remove version unpredictability from NuGet-based Hot Reload assets.

Changes:

  • Embed the Microsoft.DotNet.HotReload.WebAssembly.Browser assets into the WebAssembly SDK layout and define the JS module as a project-scoped static web asset by copying it into $(IntermediateOutputPath)hotreload\.
  • Update static web assets baselines/tests to reflect the new asset source/paths and add a new ${WebAssemblySdkPath} templatization token for stable baselines.
  • Adjust Helix test execution scripts and a couple of test asset launchSettings.json files.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/TestAssets/TestProjects/WatchBlazorWasmHosted/blazorhosted/Properties/launchSettings.json Use correct boolean type for dotnetRunMessages in test asset launch settings.
test/TestAssets/TestProjects/WatchBlazorWasm/Properties/launchSettings.json Use correct boolean type for dotnetRunMessages in test asset launch settings.
test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs Add ${WebAssemblySdkPath} tokenization to stabilize baselines referencing the WebAssembly SDK layout.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_Publish_Hosted_Works.Publish.files.json Baseline update for Hot Reload asset relocation/removal from package _content.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_Publish_DoesNotIncludeXmlDocumentationFiles_AsAssets.Publish.files.json Baseline update for Hot Reload asset relocation/removal from package _content.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_PublishMinimal_Works.Publish.files.json Baseline update for Hot Reload asset relocation/removal from package _content.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_Build_Hosted_Works.Build.staticwebassets.json Baseline update reflecting Hot Reload JS module now sourced from project intermediate output and routed under _framework.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_BuildMinimal_Works.Build.staticwebassets.json Baseline update reflecting Hot Reload JS module now sourced from project intermediate output and routed under _framework.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/StaticWebAssets_BuildMinimal_Works.Build.files.json Baseline update for new intermediate hotreload\ files.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/Publish_DoesNotGenerateManifestJson_IncludesJSModulesOnBlazorBootJsonManifest.Publish.staticwebassets.json Baseline update for JS module manifest/endpoints after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/Publish_DoesNotGenerateManifestJson_IncludesJSModulesOnBlazorBootJsonManifest.Publish.files.json Baseline update for publish output files after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JsModules_Hosted_CanCustomizeBlazorInitialization.Publish.files.json Baseline update for publish output files after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JsModules_CanHaveDifferentBuildAndPublishModules.Publish.staticwebassets.json Baseline update for manifest/endpoints after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JsModules_CanHaveDifferentBuildAndPublishModules.Publish.files.json Baseline update for publish output files after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JsModules_CanCustomizeBlazorInitialization.Publish.staticwebassets.json Baseline update for manifest/endpoints after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JsModules_CanCustomizeBlazorInitialization.Publish.files.json Baseline update for publish output files after Hot Reload asset relocation.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/JSModules_ManifestIncludesModuleTargetPaths.Build.staticwebassets.json Baseline update reflecting Hot Reload JS module now under _framework and intermediate output.
test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/StaticWebAssetsBaselines/Build_DoesNotGenerateManifestJson_IncludesJSModulesOnBlazorBootJsonManifest.Build.staticwebassets.json Baseline update for build manifest/endpoints after Hot Reload asset relocation.
src/WasmSdk/Tasks/Microsoft.NET.Sdk.WebAssembly.Tasks.csproj Pack Hot Reload tool output into the WebAssembly SDK under hotreload/ and add project reference to build it.
src/WasmSdk/Sdk/Sdk.targets Replace implicit Hot Reload PackageReference behavior with SDK-embedded files, copying the JS module to intermediate output and defining it as a static web asset.
src/BuiltInTools/HotReloadAgent.WebAssembly.Browser/Microsoft.DotNet.HotReload.WebAssembly.Browser.csproj Change to non-Razor SDK project, pin to net10.0, and ensure wwwroot content is copied to output for SDK embedding.
build/RunTestsOnHelix.sh Stop adding $DOTNET_ROOT/.nuget as a NuGet source during Helix test runs.
build/RunTestsOnHelix.cmd Stop adding %DOTNET_ROOT%\.nuget as a NuGet source during Helix test runs.

Comment on lines 44 to 45
</_WasmHotReloadModule>
<_WasmHotReloadModule OriginalItemSpec="%(Identity)" />
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

<_WasmHotReloadModule OriginalItemSpec="%(Identity)" /> is an MSBuild item element without Include/Update/Remove, which will error during evaluation (and it also won’t set OriginalItemSpec on the item created above). Use Update to set the metadata on the existing @(_WasmHotReloadModule) item(s) instead of declaring a new, empty item element.

Suggested change
</_WasmHotReloadModule>
<_WasmHotReloadModule OriginalItemSpec="%(Identity)" />
<OriginalItemSpec>%(Identity)</OriginalItemSpec>
</_WasmHotReloadModule>

Copilot uses AI. Check for mistakes.
@@ -22,7 +22,6 @@ dotnet new --debug:ephemeral-hive

# We downloaded a special zip of files to the .nuget folder so add that as a source
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This comment is now misleading: the script says the special zip was downloaded to $DOTNET_ROOT/.nuget and should be added as a source, but the command that added that source was removed. Please update the comment to match the current behavior (or re-add the source if it’s still required).

Suggested change
# We downloaded a special zip of files to the .nuget folder so add that as a source
# Add the local test packages directory as a NuGet source for this test run

Copilot uses AI. Check for mistakes.
@@ -36,7 +36,6 @@ dotnet new --debug:ephemeral-hive
REM We downloaded a special zip of files to the .nuget folder so add that as a source
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This comment is now misleading: it says a special zip was downloaded to %DOTNET_ROOT%\.nuget and should be added as a source, but the command that added that source was removed. Please update the comment to match the current behavior (or re-add the source if it’s still required).

Suggested change
REM We downloaded a special zip of files to the .nuget folder so add that as a source
REM List current NuGet sources and, if test packages are present, add them as a local source

Copilot uses AI. Check for mistakes.
@lewing lewing marked this pull request as draft February 6, 2026 19:40
@lewing
Copy link
Member Author

lewing commented Feb 6, 2026

This needs review from @maraf and probably some fixes around the baselines

- Fix invalid MSBuild ItemGroup syntax: use Update instead of creating empty item
- Update comments in test scripts to reflect actual behavior
- Addresses PR review feedback from copilot-pull-request-reviewer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants