Draft
Fix hosted Blazor WASM HTML placeholder publishing#55907
Conversation
Co-authored-by: maraf <10020471+maraf@users.noreply.github.com>
Co-authored-by: maraf <10020471+maraf@users.noreply.github.com>
Co-authored-by: maraf <10020471+maraf@users.noreply.github.com>
Co-authored-by: maraf <10020471+maraf@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
maraf
August 24, 2026 16:39
View session
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
`<StaticWebAsset Update="@(_HtmlFilesToRemove)">` inside a target's ItemGroup applied `AssetKind=Publish` and `HtmlAssetPlaceholderSource=true` to the entire StaticWebAsset item list instead of only the referenced HTML assets. The contaminated metadata was persisted into the build manifest, so at publish time `dotnet.native.js` came back as `AssetKind=Publish`. That made it eligible for `_WasmPublishAsset` alongside the copy produced by `ProcessPublishFilesForWasm`, causing GenerateWasmBootJson to throw "An item with the same key has already been added". Replace the Update with Remove + re-Include of a dedicated item list, which scopes the metadata to exactly the HTML placeholder sources. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea27dbe7-6b4d-4c52-9c7b-7a0e53e340cb
Add coverage for the scenarios the placeholder fix touches: - A no-build publish variant that builds the Server first, deletes the publish output, then publishes with NoBuild=true. This exercises the path where publish must rewrite the HTML from the persisted build manifest rather than from in-memory build state. - Assert the compressed index.html.gz / index.html.br payloads decompress to exactly the rewritten HTML, so compressed variants cannot go stale against the placeholder-replaced document. - Assert the Client build manifest keeps every WasmResource asset as a Build asset and that index.html is the only Publish asset. This guards the regression fixed in the previous commit, where placeholder metadata leaked onto unrelated assets and produced duplicate boot resources. The publish assertions also now verify the rewritten script/preload/import map entries resolve to real endpoints and files on disk. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea27dbe7-6b4d-4c52-9c7b-7a0e53e340cb
Contributor
|
Thanks for your PR, @copilot. |
Build compression had no AssetKind guard, unlike the publish side which filters _CandidateAssetsForPublish on '%(AssetKind)' != 'Build'. That gap was harmless only because publish-only assets did not previously exist at build time. The HTML placeholder source is the first one, so the missing guard started producing compressed variants of an asset that intentionally has no endpoints during build, which made ApplyCompressionNegotiation throw. Filter on '%(AssetKind)' != 'Publish' instead of the HtmlAssetPlaceholderSource marker. This restores the build/publish symmetry, states the rule once for all publish-only assets rather than for one feature, and drops compression's knowledge of the HTML placeholder feature. HtmlAssetPlaceholderSource is removed since those two conditions were its only consumers. It was never part of the StaticWebAsset schema, so it was dropped when the manifest was written and only survived in memory within a single project instance, which made it a poor thing to hang the invariant on. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea27dbe7-6b4d-4c52-9c7b-7a0e53e340cb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes dotnet/runtime#121121
Hosted Blazor WASM publishing used a separate project instance that lost the transient HTML placeholder input created during build. As a result, server publish output retained empty import maps and fingerprint placeholders.
Persist placeholder source
AssetKind=Publishstatic web asset in the build manifest.Buildslot.Publish reconstruction
Compression
Scoping the re-tag to the placeholder source assets
The first iteration re-tagged the original HTML with
Update:Inside a target,
Update="@(items)"applies the metadata to the entireStaticWebAssetitem list, not just the referenced items. Every static web asset — including all 212WasmResourceassets — was flipped toAssetKind=Publishand persisted that way into the on-disk build manifest. At publish time those assets then matched dotnet/runtime's_WasmPublishAssetfilter (AssetKind != Build and AssetTraitName == WasmResource) in addition to the copy produced byProcessPublishFilesForWasm, soGenerateWasmBootJsonhit a duplicate dictionary key and failed the build withSystem.ArgumentException: An item with the same key has already been added.MatchOnMetadata/MatchOnMetadataOptionsdo not help here — they are matching controls forRemove, not a scoping mechanism forUpdate, and silently no-op. The fix builds a dedicated item list and doesRemove+Includeinstead:Regression coverage
NoBuild=truevariant that builds the Server, deletes the publish output, then publishes. This covers the path where publish must rewrite the HTML from the persisted build manifest rather than from in-memory build state — the scenario the placeholder-source persistence exists for.index.html.gz/index.html.brpayloads decompress to exactly the rewritten HTML, so compressed variants cannot go stale against the placeholder-replaced document.WasmResourceasset as aBuildasset and thatindex.htmlis the onlyPublishasset. This is a direct guard against the metadata leak described above.