From 53dd89a41d4c3fa9553636746e7074236e583a99 Mon Sep 17 00:00:00 2001
From: Javier Calvarro Nelson <jacalvar@microsoft.com>
Date: Thu, 5 Dec 2019 05:50:03 -0800
Subject: [PATCH] [Static web assets][Fixes #AspNetCore/17079]
 PublishSingleFile results in 404 errors for Nuget components

---
 ...soft.NET.Sdk.Razor.StaticWebAssets.targets |  5 ++--
 .../MSBuildIntegrationTestBase.cs             |  2 ++
 .../StaticWebAssetsIntegrationTest.cs         | 25 +++++++++++++++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/Razor/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets b/src/Razor/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
index a200ec63ab8..693b0d87dd0 100644
--- a/src/Razor/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
+++ b/src/Razor/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
@@ -506,14 +506,15 @@ Copyright (c) .NET Foundation. All rights reserved.
 
         <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
         <RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))'))</RelativePath>
-
       </_ExternalPublishStaticWebAsset>
 
       <!-- Remove any existing external static web asset that might have been added as part of the
            regular publish pipeline. -->
       <ResolvedFileToPublish Remove="@(_ExternalPublishStaticWebAsset)" />
 
-      <ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)" />
+      <ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)">
+        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
+      </ResolvedFileToPublish>
     </ItemGroup>
 
   </Target>
diff --git a/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildIntegrationTestBase.cs b/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildIntegrationTestBase.cs
index 34c4b87961a..ddc33fb6f07 100644
--- a/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildIntegrationTestBase.cs
+++ b/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildIntegrationTestBase.cs
@@ -40,6 +40,8 @@ protected MSBuildIntegrationTestBase(BuildServerTestFixtureBase buildServer)
 
         protected string PublishOutputPath => Path.Combine(OutputPath, "publish");
 
+        protected string GetRidSpecificPublishOutputPath(string rid) => Path.Combine(OutputPath, rid, "publish");
+
         // Used by the test framework to set the project that we're working with
         internal static ProjectDirectory Project
         {
diff --git a/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/StaticWebAssetsIntegrationTest.cs b/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/StaticWebAssetsIntegrationTest.cs
index bc8b17d7053..fa747f98b3e 100644
--- a/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/StaticWebAssetsIntegrationTest.cs
+++ b/src/Razor/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/StaticWebAssetsIntegrationTest.cs
@@ -83,6 +83,31 @@ public async Task Publish_CopiesStaticWebAssetsToDestinationFolder()
             Assert.FileDoesNotExist(result, PublishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
         }
 
+        [Fact]
+        [InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
+        public async Task Publish_CopiesStaticWebAssetsToDestinationFolder_PublishSingleFile()
+        {
+            var runtimeIdentifier = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx-x64" : "linux-x64");
+            var result = await DotnetMSBuild("Publish", $"/restore /p:PublishSingleFile=true /p:RuntimeIdentifier={runtimeIdentifier}");
+
+            Assert.BuildPassed(result);
+            var publishOutputPath = GetRidSpecificPublishOutputPath(runtimeIdentifier);
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
+            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
+
+            // Validate that static web assets don't get published as content too on their regular path
+            Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
+            Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.v4.js"));
+
+            // Validate that the manifest never gets copied
+            Assert.FileDoesNotExist(result, publishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
+        }
+
         [Fact]
         [InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
         public async Task Publish_WithBuildReferencesDisabled_CopiesStaticWebAssetsToDestinationFolder()