Static Web Assets: apply deferred asset group resolution during publish#54941
Static Web Assets: apply deferred asset group resolution during publish#54941Copilot wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a build→publish inconsistency in the Static Web Assets pipeline by persisting resolved (non-deferred) asset group decisions into the build manifest and then re-applying that decision during publish when the build manifest is loaded, preventing duplicate assets/endpoints from surviving into publish and causing endpoint manifest generation failures.
Changes:
- Persist resolved
StaticWebAssetGroupdata intostaticwebassets.build.jsonand expose it when reading manifests. - Re-apply group filtering during publish after reloading the build manifest (including in-process build+publish via cached groups).
- Add an integration test covering the publish scenario where two variants collapse to the same route and only one should survive.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.NET.Sdk.StaticWebAssets.Tests/DeferredAssetGroupsIntegrationTest.cs | Adds a publish regression test validating only one grouped variant/endpoints survive publish. |
| src/StaticWebAssetsSdk/Tasks/ReadStaticWebAssetsManifestFile.cs | Adds StaticWebAssetGroups output when reading a manifest. |
| src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetsManifest.cs | Captures @(StaticWebAssetGroup) input and persists resolved groups into the manifest. |
| src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetGroup.cs | Makes StaticWebAssetGroup public/serializable with task-item roundtrip + value equality/hash. |
| src/StaticWebAssetsSdk/Tasks/Data/StaticAssetsManifest.cs | Adds Groups to the build manifest model, hash computation, and equality/hash code. |
| src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets | Passes groups into build manifest generation and caches build-time groups for in-proc publish. |
| src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Publish.targets | Reloads groups from build manifest and re-filters assets/endpoints during publish using persisted groups. |
| var publish = CreatePublishCommand(projectDirectory, "AppWithP2PReference"); | ||
| ExecuteCommand(publish).Should().Pass(); | ||
|
|
||
| var intermediateOutputPath = publish.GetIntermediateDirectory(DefaultTfm, "Debug").ToString(); |
…otnet/sdk#54941) Reverts the framework-asset modeling and the in-package publish workaround. With the SDK fix in dotnet/sdk#54941 (resolved deferred static web asset groups are persisted into the build manifest and re-applied, unscoped, when the manifest is reloaded at publish), the idiomatic deferred-group authoring that the WebView package already uses is correct end-to-end: blazor.modules.json is a Package static web asset in the deferred BlazorWebViewModules group, resolved to drop the fallback when the app contributes its own JS modules and keep it otherwise. No framework-asset hack and no package-local workaround are needed, so the WebView product files are unchanged from main. This PR now contributes the static web assets packaging + build/publish regression tests: - Package-layout tests assert the deferred-group shape for WebView (modules.json = Package in BlazorWebViewModules=fallback; webview.js = Framework) and the framework/group shapes for Components.WebAssembly, App.Internal.Assets and Identity.UI. - Build/publish behavior tests (package consumer and ProjectReference consumer) assert a single _framework/blazor.modules.json endpoint and that the app's manifest supersedes the fallback. The publish assertions are skipped until dotnet/sdk#54941 is in the repo SDK (detected via the pre-fix 'Sequence contains more than one element' crash) so the suite stays green meanwhile. Note: this PR depends on dotnet/sdk#54941 flowing into the repo SDK; until then the in-repo WebView publish path (and the publish tests) require that fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added |
#67374) The WebView package ships a fallback _framework/blazor.modules.json (empty []) for apps that contribute no JS library modules. Modeling it as a deferred static web asset group required tagging/promoting the consumer's SDK-generated manifest and depended on an SDK fix (dotnet/sdk#54941) to filter the group at publish; without it, publish crashed with "Sequence contains more than one element" (#67374). Replace the group authoring with conditional materialization: the package ships the fallback raw under build/ (not as a flowing static web asset) and materializes it as the consumer's own asset during ResolveStaticWebAssetsInputs ONLY when the app has no JS modules of its own. The decision runs before the build manifest / conflict check, so exactly one asset ever lands on _framework/blazor.modules.json and there is never a conflict at build or publish. No asset groups, no consumer manifest tagging, no SDK dependency. Tests validate the package layout and build/publish behavior (package + P2P consumers, with and without JS modules) by cracking the built .nupkg and running isolated consumer builds under artifacts/ with binlog capture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nifest groups Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
…fest Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
…upProject Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
5415398 to
d8a29aa
Compare
…otnet/sdk#54941) Reverts the framework-asset modeling and the in-package publish workaround. With the SDK fix in dotnet/sdk#54941 (resolved deferred static web asset groups are persisted into the build manifest and re-applied, unscoped, when the manifest is reloaded at publish), the idiomatic deferred-group authoring that the WebView package already uses is correct end-to-end: blazor.modules.json is a Package static web asset in the deferred BlazorWebViewModules group, resolved to drop the fallback when the app contributes its own JS modules and keep it otherwise. No framework-asset hack and no package-local workaround are needed, so the WebView product files are unchanged from main. This PR now contributes the static web assets packaging + build/publish regression tests: - Package-layout tests assert the deferred-group shape for WebView (modules.json = Package in BlazorWebViewModules=fallback; webview.js = Framework) and the framework/group shapes for Components.WebAssembly, App.Internal.Assets and Identity.UI. - Build/publish behavior tests (package consumer and ProjectReference consumer) assert a single _framework/blazor.modules.json endpoint and that the app's manifest supersedes the fallback. The publish assertions are skipped until dotnet/sdk#54941 is in the repo SDK (detected via the pre-fix 'Sequence contains more than one element' crash) so the suite stays green meanwhile. Note: this PR depends on dotnet/sdk#54941 flowing into the repo SDK; until then the in-repo WebView publish path (and the publish tests) require that fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#67374) The WebView package ships a fallback _framework/blazor.modules.json (empty []) for apps that contribute no JS library modules. Modeling it as a deferred static web asset group required tagging/promoting the consumer's SDK-generated manifest and depended on an SDK fix (dotnet/sdk#54941) to filter the group at publish; without it, publish crashed with "Sequence contains more than one element" (#67374). Replace the group authoring with conditional materialization: the package ships the fallback raw under build/ (not as a flowing static web asset) and materializes it as the consumer's own asset during ResolveStaticWebAssetsInputs ONLY when the app has no JS modules of its own. The decision runs before the build manifest / conflict check, so exactly one asset ever lands on _framework/blazor.modules.json and there is never a conflict at build or publish. No asset groups, no consumer manifest tagging, no SDK dependency. Tests validate the package layout and build/publish behavior (package + P2P consumers, with and without JS modules) by cracking the built .nupkg and running isolated consumer builds under artifacts/ with binlog capture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deferred Static Web Asset groups were resolved during
buildbut never duringpublish. When two grouped variants resolved to the same route withAssetKind=All(e.g. a package fallback plus an app-generated asset), the variant the group was supposed to exclude survived into the publish pipeline andGenerateStaticWebAssetEndpointsManifestthrewInvalidOperationException: Sequence contains more than one element. The same project built successfully.Root cause
The build manifest intentionally retains all group variants so transitive consumers can re-resolve against their own graph. Publish reloads that manifest but:
FilterDeferredStaticWebAssetGroups), andFilterStaticWebAssetGroupsis consumer-scoped (Source="$(PackageId)"), so it structurally cannot filter groups owned by a referenced project or NuGet package.Re-resolving at publish from the package can't fix this — the decision has to be made where the filter is unscoped (build) and carried forward.
Changes
StaticWebAssetGroupis now public/serializable with round-trip (ToTaskItem/FromItemGroupToArray) and value equality;StaticAssetsManifestgains aGroupsproperty wired intoCreate/hash/equals.GenerateStaticWebAssetsManifestwrites the resolved (no-longer-Deferred) groups intostaticwebassets.build.json.ReadStaticWebAssetsManifestFileexposes a newStaticWebAssetGroupsoutput.LoadStaticWebAssetsBuildManifestre-runsFilterStaticWebAssetGroups(unscoped) using the persisted groups, dropping the excluded variant before the publish endpoints manifest is computed. Build-time groups are also cached (_CachedBuildStaticWebAssetGroups) so the in-process build+publish path is covered alongside fresh/no-build publishes that read from the manifest.publishcase toDeferredAssetGroupsIntegrationTestwhere twoAssetKind=Allvariants collapse to one route, owned by a referenced project, and asserts exactly one asset/endpoint survives.Because the build manifest still retains all variants, transitive consumers continue to re-import the deferred group definitions and re-resolve against their own graph; persistence only governs the current project's build→publish consistency.