From 4abee8315142d7fd2315128a9fd18291790c7b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Mon, 19 Jan 2026 12:01:15 +0100 Subject: [PATCH 01/13] Revert duplicate file name validation Removed duplicate file name check for asset candidates. --- .../ComputeWasmBuildAssets.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 2eafa784282373..68ac26ef49d3bf 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -152,14 +152,6 @@ public override bool Execute() Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); } - // Check for unique file name before adding candidate - var candidateFileName = Path.GetFileName(candidate.ItemSpec); - if (!uniqueFileNames.Add(candidateFileName)) - { - Log.LogMessage(MessageImportance.Low, "Skipping duplicate file name '{0}' for candidate '{1}'", candidateFileName, candidate.ItemSpec); - continue; - } - assetCandidates.Add(candidate); } From 59a11cfaf88ff81407d3f69d4696cff4ae37ec97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 12:29:31 +0100 Subject: [PATCH 02/13] Return from the loop once satellite assembly is processed --- .../ComputeWasmBuildAssets.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 68ac26ef49d3bf..a8e99d85412995 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -150,6 +150,17 @@ public override bool Execute() candidate.SetMetadata("RelatedAsset", relatedAssetPath); Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); + + assetCandidates.Add(candidate); + continue; + } + + // Check for unique relative path before adding candidate. + var candidateFileName = Path.GetFileName(candidate.ItemSpec); + if (!uniqueFileNames.Add(candidateFileName)) + { + Log.LogMessage(MessageImportance.Low, "Skipping duplicate file name '{0}' for candidate '{1}'", candidateFileName, candidate.ItemSpec); + continue; } assetCandidates.Add(candidate); From 8cae7a6e1ee23760c692ad5cd5523acb9b137f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 12:41:04 +0100 Subject: [PATCH 03/13] Whitespace --- .../ComputeWasmBuildAssets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index a8e99d85412995..62ecd579a4f591 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -150,7 +150,7 @@ public override bool Execute() candidate.SetMetadata("RelatedAsset", relatedAssetPath); Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); - + assetCandidates.Add(candidate); continue; } From c5b9a4c0f6c4cc1a8cd5f18c4629cfa72bcd2e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 13:49:29 +0000 Subject: [PATCH 04/13] Copilot: Add second language to referenced resources --- .../Wasm.Build.Tests/SatelliteLoadingTests.cs | 6 +- .../App/SatelliteAssembliesTest.cs | 2 +- .../ResourceLibrary/ResourceAccessor.cs | 1 + .../ResourceLibrary/words.fr-FR.resx | 127 ++++++++++++++++++ 4 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.fr-FR.resx diff --git a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs index 90d27ad7287c76..38fa47668100a0 100644 --- a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs @@ -47,10 +47,12 @@ public async Task LoadSatelliteAssembly(bool loadAllSatelliteResources) // So there messages are should be present only when we are lazily loading satellites. expectedOutput.Add(m => Assert.Equal("default: hello", m)); expectedOutput.Add(m => Assert.Equal("es-ES without satellite: hello", m)); + expectedOutput.Add(m => Assert.Equal("fr-FR without satellite: hello", m)); } expectedOutput.Add(m => Assert.Equal("default: hello", m)); expectedOutput.Add(m => Assert.Equal("es-ES with satellite: hola", m)); + expectedOutput.Add(m => Assert.Equal("fr-FR with satellite: bonjour", m)); Assert.Collection( result.TestOutput, @@ -94,8 +96,10 @@ public async Task LoadSatelliteAssemblyFromReference() result.TestOutput, m => Assert.Equal("default: hello", m), m => Assert.Equal("es-ES without satellite: hello", m), + m => Assert.Equal("fr-FR without satellite: hello", m), m => Assert.Equal("default: hello", m), - m => Assert.Equal("es-ES with satellite: hola", m) + m => Assert.Equal("es-ES with satellite: hola", m), + m => Assert.Equal("fr-FR with satellite: bonjour", m) ); } } diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs b/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs index 8d7631e0c8f6ce..92a695d284e0cd 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs @@ -13,7 +13,7 @@ public static async Task Run(bool loadSatelliteAssemblies) if (loadSatelliteAssemblies) { ResourceLibrary.ResourceAccessor.Read(TestOutput.WriteLine, false); - await LoadSatelliteAssemblies(new[] { "es-ES" }); + await LoadSatelliteAssemblies(new[] { "es-ES", "fr-FR" }); } ResourceLibrary.ResourceAccessor.Read(TestOutput.WriteLine, true); diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs index 27073846c66ae7..7c48551cabb3d8 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs @@ -15,5 +15,6 @@ public static void Read(Action testOuput, bool hasSatellites) var rm = new ResourceManager("ResourceLibrary.words", typeof(ResourceAccessor).Assembly); testOuput($"default: {rm.GetString("hello", CultureInfo.CurrentCulture)}"); testOuput($"es-ES {(hasSatellites ? "with" : "without")} satellite: {rm.GetString("hello", new CultureInfo("es-ES"))}"); + testOuput($"fr-FR {(hasSatellites ? "with" : "without")} satellite: {rm.GetString("hello", new CultureInfo("fr-FR"))}"); } } diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.fr-FR.resx b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.fr-FR.resx new file mode 100644 index 00000000000000..61280687cdc2bd --- /dev/null +++ b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.fr-FR.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + au revoir + + + + bonjour + + From 1be509e4da131c93ac96724612f2f44f472426ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 13:54:43 +0000 Subject: [PATCH 05/13] Apply quick fix --- src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs index 38fa47668100a0..f1cf72280004b7 100644 --- a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs @@ -70,7 +70,7 @@ public async Task LoadSatelliteAssemblyFromReference() var appCsprojPath = Path.Combine(_projectDir, "WasmBasicTestApp.csproj"); var appCsproj = XDocument.Load(appCsprojPath); - var projectReference = appCsproj.Descendants("ProjectReference").Where(pr => pr.Attribute("Include")?.Value?.Contains("ResourceLibrary") ?? false).Single(); + var projectReference = appCsproj.Descendants("ProjectReference").Single(pr => pr.Attribute("Include")?.Value?.Contains("ResourceLibrary") ?? false); var itemGroup = projectReference.Parent!; projectReference.Remove(); From 4f4a66e2c3cef414a4e34c2160915c27e733f927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 13:56:18 +0000 Subject: [PATCH 06/13] Whitespace --- .../ComputeWasmBuildAssets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 62ecd579a4f591..22661820b320c5 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -154,7 +154,7 @@ public override bool Execute() assetCandidates.Add(candidate); continue; } - + // Check for unique relative path before adding candidate. var candidateFileName = Path.GetFileName(candidate.ItemSpec); if (!uniqueFileNames.Add(candidateFileName)) From ea4a578f0346b618aa2b0ec808b542b2887f6770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Tue, 20 Jan 2026 14:15:26 +0000 Subject: [PATCH 07/13] Copilot: Add test with satellites from PackageReference --- .../Wasm.Build.Tests/SatelliteLoadingTests.cs | 22 +++++++++++++++++++ .../ResourceLibrary/ResourceLibrary.csproj | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs index f1cf72280004b7..c0bc8895e093fb 100644 --- a/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/SatelliteLoadingTests.cs @@ -102,4 +102,26 @@ public async Task LoadSatelliteAssemblyFromReference() m => Assert.Equal("fr-FR with satellite: bonjour", m) ); } + + [Fact, TestCategory("no-fingerprinting")] + public void SatelliteAssembliesFromPackageReference() + { + Configuration config = Configuration.Release; + ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "SatelliteLoadingTestsFromPackageReference"); + BuildProject(info, config, new BuildOptions(ExtraMSBuildArgs: "-p:TestSatelliteAssembliesFromPackage=true")); + + string binFrameworkDir = GetBinFrameworkDir(config, forPublish: false); + + // Microsoft.CodeAnalysis.CSharp has satellite assemblies for multiple locales + // Verify that at least some of them are present in the AppBundle + string[] expectedLocales = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-Hans", "zh-Hant"]; + foreach (string locale in expectedLocales) + { + string satelliteDir = Path.Combine(binFrameworkDir, locale); + Assert.True(Directory.Exists(satelliteDir), $"Expected satellite directory '{locale}' to exist in {binFrameworkDir}"); + + string[] satelliteFiles = Directory.GetFiles(satelliteDir, "Microsoft.CodeAnalysis.CSharp.resources*"); + Assert.True(satelliteFiles.Length > 0, $"Expected Microsoft.CodeAnalysis.CSharp.resources.dll in {satelliteDir}"); + } + } } diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj index 9a4419fdc08de5..8511ea8189bd03 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj +++ b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj @@ -4,4 +4,8 @@ Library true + + + + From 16fb5d3083a385ede1fd97384229f2133f63f336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 21 Jan 2026 10:34:01 +0000 Subject: [PATCH 08/13] Revert comment change --- .../ComputeWasmBuildAssets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 22661820b320c5..a224e2e4e15469 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -155,7 +155,7 @@ public override bool Execute() continue; } - // Check for unique relative path before adding candidate. + // Check for unique file name before adding candidate var candidateFileName = Path.GetFileName(candidate.ItemSpec); if (!uniqueFileNames.Add(candidateFileName)) { From a7304256c679421863acd1b64877e9a1207a65eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Wed, 21 Jan 2026 13:36:05 +0000 Subject: [PATCH 09/13] Compute RelativePath for candidate satellite assembly --- .../ComputeWasmBuildAssets.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index a224e2e4e15469..cf5436c8fd4645 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -139,6 +139,7 @@ public override bool Execute() candidate.SetMetadata("AssetRole", "Related"); candidate.SetMetadata("AssetTraitName", "Culture"); candidate.SetMetadata("AssetTraitValue", culture); + var fileName = candidate.GetMetadata("FileName"); var suffixIndex = fileName.Length - ".resources".Length; var relatedAssetPath = Path.GetFullPath(Path.Combine( @@ -148,6 +149,7 @@ public override bool Execute() fileName.Substring(0, suffixIndex) + ProjectAssembly[0].GetMetadata("Extension"))); candidate.SetMetadata("RelatedAsset", relatedAssetPath); + candidate.SetMetadata("RelativePath", $"_framework/{culture}/{fileName}{candidate.GetMetadata("Extension")}"); Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); From a35df41bc1cdb56fcbb52b3b588bd152feb92dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 22 Jan 2026 08:35:28 +0000 Subject: [PATCH 10/13] Rewrite checking for duplicities --- .../ComputeWasmBuildAssets.cs | 85 +++++++++---------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index cf5436c8fd4645..db54c738315a60 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -66,7 +66,7 @@ public override bool Execute() { var filesToRemove = new List(); var assetCandidates = new List(); - var uniqueFileNames = new HashSet(StringComparer.OrdinalIgnoreCase); + var uniqueRelativePaths = new HashSet(StringComparer.OrdinalIgnoreCase); try { @@ -117,51 +117,50 @@ public override bool Execute() assetCandidate.SetMetadata("RelatedAsset", Path.GetFullPath(Path.Combine(OutputPath, "wwwroot", "_framework", Path.GetFileName(resolvedFrom)))); - assetCandidates.Add(assetCandidate); - continue; - } - - string relativePath = AssetsComputingHelper.GetCandidateRelativePath(candidate, FingerprintAssets, FingerprintDotNetJs); - candidate.SetMetadata("RelativePath", relativePath); - - // Workaround for https://github.com/dotnet/aspnetcore/issues/37574. - // For items added as "Reference" in project references, the OriginalItemSpec is incorrect. - // Ignore it, and use the FullPath instead. - if (candidate.GetMetadata("ReferenceSourceTarget") == "ProjectReference") - { - candidate.SetMetadata("OriginalItemSpec", candidate.ItemSpec); + candidate = assetCandidate; } - - var culture = candidate.GetMetadata("Culture"); - if (!string.IsNullOrEmpty(culture)) + else { - candidate.SetMetadata("AssetKind", "Build"); - candidate.SetMetadata("AssetRole", "Related"); - candidate.SetMetadata("AssetTraitName", "Culture"); - candidate.SetMetadata("AssetTraitValue", culture); - - var fileName = candidate.GetMetadata("FileName"); - var suffixIndex = fileName.Length - ".resources".Length; - var relatedAssetPath = Path.GetFullPath(Path.Combine( - OutputPath, - "wwwroot", - "_framework", - fileName.Substring(0, suffixIndex) + ProjectAssembly[0].GetMetadata("Extension"))); - - candidate.SetMetadata("RelatedAsset", relatedAssetPath); - candidate.SetMetadata("RelativePath", $"_framework/{culture}/{fileName}{candidate.GetMetadata("Extension")}"); - - Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); - - assetCandidates.Add(candidate); - continue; + var culture = candidate.GetMetadata("Culture"); + if (!string.IsNullOrEmpty(culture)) + { + candidate.SetMetadata("AssetKind", "Build"); + candidate.SetMetadata("AssetRole", "Related"); + candidate.SetMetadata("AssetTraitName", "Culture"); + candidate.SetMetadata("AssetTraitValue", culture); + + var fileName = candidate.GetMetadata("FileName"); + var suffixIndex = fileName.Length - ".resources".Length; + var relatedAssetPath = Path.GetFullPath(Path.Combine( + OutputPath, + "wwwroot", + "_framework", + fileName.Substring(0, suffixIndex) + ProjectAssembly[0].GetMetadata("Extension"))); + + candidate.SetMetadata("RelatedAsset", relatedAssetPath); + candidate.SetMetadata("RelativePath", $"_framework/{culture}/{fileName}{candidate.GetMetadata("Extension")}"); + + Log.LogMessage(MessageImportance.Low, "Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); + } + else + { + // Workaround for https://github.com/dotnet/aspnetcore/issues/37574. + // For items added as "Reference" in project references, the OriginalItemSpec is incorrect. + // Ignore it, and use the FullPath instead. + if (candidate.GetMetadata("ReferenceSourceTarget") == "ProjectReference") + { + candidate.SetMetadata("OriginalItemSpec", candidate.ItemSpec); + } + + string relativePath = AssetsComputingHelper.GetCandidateRelativePath(candidate, FingerprintAssets, FingerprintDotNetJs); + candidate.SetMetadata("RelativePath", relativePath); + } } - // Check for unique file name before adding candidate - var candidateFileName = Path.GetFileName(candidate.ItemSpec); - if (!uniqueFileNames.Add(candidateFileName)) + var relativePath = candidate.GetMetadata("RelativePath"); + if (!uniqueRelativePaths.Add(relativePath)) { - Log.LogMessage(MessageImportance.Low, "Skipping duplicate file name '{0}' for candidate '{1}'", candidateFileName, candidate.ItemSpec); + Log.LogMessage(MessageImportance.Low, "Skipping duplicate relative path '{0}' for candidate '{1}'", relativePath, candidate.ItemSpec); continue; } @@ -196,13 +195,13 @@ public override bool Execute() "_framework", ProjectAssembly[0].GetMetadata("FileName") + ProjectAssembly[0].GetMetadata("Extension"))); - var normalizedPath = assetCandidate.GetMetadata("TargetPath").Replace('\\', '/'); + var relativePath = Path.Combine("_framework", assetCandidate.GetMetadata("TargetPath").Replace('\\', '/')); assetCandidate.SetMetadata("AssetKind", "Build"); assetCandidate.SetMetadata("AssetRole", "Related"); assetCandidate.SetMetadata("AssetTraitName", "Culture"); assetCandidate.SetMetadata("AssetTraitValue", candidateCulture); - assetCandidate.SetMetadata("RelativePath", Path.Combine("_framework", normalizedPath)); + assetCandidate.SetMetadata("RelativePath", relativePath); assetCandidate.SetMetadata("RelatedAsset", projectAssemblyAssetPath); assetCandidates.Add(assetCandidate); From b07787c938096964303f288964ebe0dc0c94dbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 22 Jan 2026 13:57:56 +0100 Subject: [PATCH 11/13] Fix --- .../ComputeWasmBuildAssets.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index db54c738315a60..a5826091849ea1 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -152,8 +152,7 @@ public override bool Execute() candidate.SetMetadata("OriginalItemSpec", candidate.ItemSpec); } - string relativePath = AssetsComputingHelper.GetCandidateRelativePath(candidate, FingerprintAssets, FingerprintDotNetJs); - candidate.SetMetadata("RelativePath", relativePath); + candidate.SetMetadata("RelativePath", AssetsComputingHelper.GetCandidateRelativePath(candidate, FingerprintAssets, FingerprintDotNetJs)); } } From 812904242a9c1f2e9487cfefd9696066e928039d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 22 Jan 2026 14:57:10 +0000 Subject: [PATCH 12/13] Fix --- .../ComputeWasmBuildAssets.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index a5826091849ea1..405e596c1dd880 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -195,6 +195,11 @@ public override bool Execute() ProjectAssembly[0].GetMetadata("FileName") + ProjectAssembly[0].GetMetadata("Extension"))); var relativePath = Path.Combine("_framework", assetCandidate.GetMetadata("TargetPath").Replace('\\', '/')); + if (!uniqueRelativePaths.Add(relativePath)) + { + Log.LogMessage(MessageImportance.Low, "Skipping duplicate relative path '{0}' for candidate '{1}'", relativePath, projectSatelliteAssembly.ItemSpec); + continue; + } assetCandidate.SetMetadata("AssetKind", "Build"); assetCandidate.SetMetadata("AssetRole", "Related"); From 36b2eb97a253ab7c7be0b95221bdfe17189130fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 22 Jan 2026 19:09:26 +0000 Subject: [PATCH 13/13] Fix direction of slash --- .../ComputeWasmBuildAssets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 405e596c1dd880..34dcf1647aa3e2 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -194,7 +194,7 @@ public override bool Execute() "_framework", ProjectAssembly[0].GetMetadata("FileName") + ProjectAssembly[0].GetMetadata("Extension"))); - var relativePath = Path.Combine("_framework", assetCandidate.GetMetadata("TargetPath").Replace('\\', '/')); + var relativePath = $"_framework/{assetCandidate.GetMetadata("TargetPath").Replace('\\', '/')}"; if (!uniqueRelativePaths.Add(relativePath)) { Log.LogMessage(MessageImportance.Low, "Skipping duplicate relative path '{0}' for candidate '{1}'", relativePath, projectSatelliteAssembly.ItemSpec);