diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs index 7c6205d327..3ab75b0452 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; @@ -85,7 +86,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks } } - return nodes; + // Its important that we order the nodes here to produce a manifest deterministically. + return nodes.OrderBy(e=>e.Attribute(BasePath).Value); } private XmlWriter GetXmlWriter(XmlWriterSettings settings) diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAsssetsPropsFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAsssetsPropsFile.cs new file mode 100644 index 0000000000..4146eed9fa --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAsssetsPropsFile.cs @@ -0,0 +1,159 @@ +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Razor.Tasks +{ + public class GenerateStaticWebAsssetsPropsFile : Task + { + private const string SourceType = "SourceType"; + private const string SourceId = "SourceId"; + private const string ContentRoot = "ContentRoot"; + private const string BasePath = "BasePath"; + private const string RelativePath = "RelativePath"; + + [Required] + public string TargetPropsFilePath { get; set; } + + [Required] + public ITaskItem[] StaticWebAssets { get; set; } + + public override bool Execute() + { + if (!ValidateArguments()) + { + return false; + } + + return ExecuteCore(); + } + + private bool ExecuteCore() + { + if (StaticWebAssets.Length == 0) + { + return !Log.HasLoggedErrors; + } + + var template = StaticWebAssets[0]; + + var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); + var root = new XElement( + "Project", + new XElement("ItemGroup", + new XElement("StaticWebAsset", + new XAttribute("Include", @"$(MSBuildThisFileDirectory)..\staticwebassets\**"), + new XElement(SourceType, "Package"), + new XElement(SourceId, template.GetMetadata(SourceId)), + new XElement(ContentRoot, @"$(MSBuildThisFileDirectory)..\staticwebassets\"), + new XElement(BasePath, template.GetMetadata(BasePath)), + new XElement(RelativePath, "%(RecursiveDir)%(FileName)%(Extension)")))); + + document.Add(root); + + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CloseOutput = true, + OmitXmlDeclaration = true, + Indent = true, + NewLineOnAttributes = false, + Async = true + }; + + using (var xmlWriter = GetXmlWriter(settings)) + { + document.WriteTo(xmlWriter); + } + + return !Log.HasLoggedErrors; + } + + private XmlWriter GetXmlWriter(XmlWriterSettings settings) + { + var fileStream = new FileStream(TargetPropsFilePath, FileMode.Create); + return XmlWriter.Create(fileStream, settings); + } + + private bool ValidateArguments() + { + ITaskItem firstAsset = null; + + for (var i = 0; i < StaticWebAssets.Length; i++) + { + var webAsset = StaticWebAssets[i]; + if (!EnsureRequiredMetadata(webAsset, SourceId) || + !EnsureRequiredMetadata(webAsset, SourceType, allowEmpty: true) || + !EnsureRequiredMetadata(webAsset, ContentRoot) || + !EnsureRequiredMetadata(webAsset, BasePath) || + !EnsureRequiredMetadata(webAsset, RelativePath)) + { + return false; + } + + if (firstAsset == null) + { + firstAsset = webAsset; + continue; + } + + if (!ValidateMetadataMatches(firstAsset, webAsset, SourceId) || + !ValidateMetadataMatches(firstAsset, webAsset, SourceType) || + !ValidateMetadataMatches(firstAsset, webAsset, ContentRoot) || + !ValidateMetadataMatches(firstAsset, webAsset, BasePath)) + { + return false; + } + } + + return true; + } + + private bool ValidateMetadataMatches(ITaskItem reference, ITaskItem candidate, string metadata) + { + var referenceMetadata = reference.GetMetadata(metadata); + var candidateMetadata = candidate.GetMetadata(metadata); + if (!string.Equals(referenceMetadata, candidateMetadata, System.StringComparison.Ordinal)) + { + Log.LogError($"Static web assets have different '{metadata}' metadata values '{referenceMetadata}' and '{candidateMetadata}' for '{reference.ItemSpec}' and '{candidate.ItemSpec}'."); + return false; + } + + return true; + } + + private bool EnsureRequiredMetadata(ITaskItem item, string metadataName, bool allowEmpty = false) + { + var value = item.GetMetadata(metadataName); + var isInvalidValue = allowEmpty ? !HasMetadata(item, metadataName) : string.IsNullOrEmpty(value); + + if (isInvalidValue) + { + Log.LogError($"Missing required metadata '{metadataName}' for '{item.ItemSpec}'."); + return false; + } + + return true; + } + + private bool HasMetadata(ITaskItem item, string metadataName) + { + foreach (var name in item.MetadataNames) + { + if (string.Equals(metadataName, (string)name, StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/StaticWebAssetsGeneratePackagePropsFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/StaticWebAssetsGeneratePackagePropsFile.cs new file mode 100644 index 0000000000..bf7c78ab35 --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/StaticWebAssetsGeneratePackagePropsFile.cs @@ -0,0 +1,54 @@ +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Razor.Tasks +{ + public class StaticWebAssetsGeneratePackagePropsFile : Task + { + [Required] + public string PropsFileImport { get; set; } + + [Required] + public string BuildTargetPath { get; set; } + + public override bool Execute() + { + var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); + var root = new XElement( + "Project", + new XElement("Import", + new XAttribute("Project", PropsFileImport))); + + document.Add(root); + + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CloseOutput = true, + OmitXmlDeclaration = true, + Indent = true, + NewLineOnAttributes = false, + Async = true + }; + + using (var xmlWriter = GetXmlWriter(settings)) + { + document.WriteTo(xmlWriter); + } + + return !Log.HasLoggedErrors; + } + + private XmlWriter GetXmlWriter(XmlWriterSettings settings) + { + var fileStream = new FileStream(BuildTargetPath, FileMode.Create); + return XmlWriter.Create(fileStream, settings); + } + } +} \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets index 3d403100a6..e83d89a7f2 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets @@ -20,6 +20,11 @@ Copyright (c) .NET Foundation. All rights reserved. * Current project. * Referenced project. * Referenced packages. + * GenerateStaticWebAssetsPackTargets: Includes the static web assets in the current project + under the 'staticwebassets' folder in the nuget package and generates and includes in the + package the appropriate .props files to support discovering the packaged static web assets. + * GetCurrentProjectStaticWebAssets: Called on each referenced project to retrieve the list of + static web assets in the project. --> + + + + ResolveStaticWebAssetsInputs; @@ -49,25 +64,76 @@ Copyright (c) .NET Foundation. All rights reserved. $(AssignTargetPathsDependsOn) - + _ResolveStaticWebAssetsProjectReferences; $(ResolveStaticWebAssetsInputsDependsOn) + + _CreateStaticWebAssetsCustomPropsCacheFile; + $(GenerateStaticWebAssetsPackTargetsDependsOn) + + + + GenerateStaticWebAssetsPackTargets; + $(TargetsForTfmSpecificContentInPackage) + + + + _RemoveWebRootContentFromPackaging; + $(PackDependsOn) + + - <_GeneratedStaticWebAssetsInputsCacheFile>$(IntermediateOutputPath)$(TargetName).StaticWebAssets.cache - <_GeneratedStaticWebAssetsDevelopmentManifest>$(IntermediateOutputPath)$(TargetName).StaticWebAssets.xml + <_StaticWebAssetsIntermediateOutputPath>$(IntermediateOutputPath)staticwebassets\ + + + <_GeneratedStaticWebAssetsInputsCacheFile>$(_StaticWebAssetsIntermediateOutputPath)$(TargetName).StaticWebAssets.Manifest.cache + <_GeneratedStaticWebAssetsDevelopmentManifest>$(_StaticWebAssetsIntermediateOutputPath)$(TargetName).StaticWebAssets.xml + + + <_GeneratedStaticWebAssetsCustomPropsCacheFile>$(_StaticWebAssetsIntermediateOutputPath)$(TargetName).StaticWebAssets.Pack.cache + + + <_GeneratedStaticWebAssetsPropsFile>$(_StaticWebAssetsIntermediateOutputPath)msbuild.$(PackageId).Microsoft.AspNetCore.StaticWebAssets.props + <_GeneratedBuildPropsFile>$(_StaticWebAssetsIntermediateOutputPath)msbuild.build.$(PackageId).props + <_GeneratedBuildMultitargetingPropsFile>$(_StaticWebAssetsIntermediateOutputPath)msbuild.buildMultiTargeting.$(PackageId).props + <_GeneratedBuildTransitivePropsFile>$(_StaticWebAssetsIntermediateOutputPath)msbuild.buildTransitive.$(PackageId).props + + + <_StaticWebAssetsPropsFileImportPath>Microsoft.AspNetCore.StaticWebAssets.props + <_StaticWebAssetsGeneratedBuildPropsFileImportPath>..\build\$(PackageId).props + <_StaticWebAssetsGeneratedBuildMultiTargetingPropsFileImportPath>..\buildMultiTargeting\$(PackageId).props + - + + + + + + + - + DependsOnTargets="ResolveStaticWebAssetsInputs;_PrepareForStaticWebAssets"> + - <_ExternalStaticWebAsset Include="%(StaticWebAsset.Identity)" @@ -82,10 +148,10 @@ Copyright (c) .NET Foundation. All rights reserved. - @@ -94,7 +160,7 @@ Copyright (c) .NET Foundation. All rights reserved. - - - - - + + + + + + PreserveNewest + Never + @@ -127,6 +203,15 @@ Copyright (c) .NET Foundation. All rights reserved. + + - @@ -164,7 +249,7 @@ Copyright (c) .NET Foundation. All rights reserved. <_ThisProjectStaticWebAsset Include="$(MSBuildProjectDirectory)\wwwroot\**" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" /> - $(PackageId) - + + + + + + + + + + + + + + <_CurrentProjectStaticWebAsset + Include="@(StaticWebAsset)" + Condition="'%(SourceType)' == ''" /> + + + + + + + + + + + + + + + + + + + <_CurrentProjectHasStaticWebAssets Condition="'@(_CurrentProjectStaticWebAsset->Count())' != '0'">true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + build\Microsoft.AspNetCore.StaticWebAssets.props + + + + + build\$(PackageId).props + + + + + buildMultiTargeting\$(PackageId).props + + + + + buildTransitive\$(PackageId).props + + + + + + staticwebassets\%(_CurrentProjectStaticWebAsset.RelativePath) + + + + + + + + + + + + + + + <_ExternalPublishStaticWebAsset + Include="%(StaticWebAsset.FullPath)" + Condition="'%(SourceType)' != ''"> + + PreserveNewest + $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))')) + + + + + + + + + + + \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props index c1a728ae5b..91dd8fc557 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props @@ -75,7 +75,7 @@ Copyright (c) .NET Foundation. All rights reserved. - true + true = '3.0')">true + + + $(_Targeting30OrNewerRazorLangVersion) @@ -338,7 +342,7 @@ Copyright (c) .NET Foundation. All rights reserved. - + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateStaticWebAssetsPropsFileTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateStaticWebAssetsPropsFileTest.cs new file mode 100644 index 0000000000..af181b23dc --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateStaticWebAssetsPropsFileTest.cs @@ -0,0 +1,433 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Tasks +{ + public class GenerateStaticWebAssetsPropsFileTest + { + [Fact] + public void Fails_WhenStaticWebAsset_DoesNotContainSourceType() + { + // Arrange + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal($"Missing required metadata 'SourceType' for '{Path.Combine("wwwroot", "js", "sample.js")}'.", message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_DoesNotContainSourceId() + { + // Arrange + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal($"Missing required metadata 'SourceId' for '{Path.Combine("wwwroot", "js", "sample.js")}'.", message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_DoesNotContainContentRoot() + { + // Arrange + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal($"Missing required metadata 'ContentRoot' for '{Path.Combine("wwwroot", "js", "sample.js")}'.", message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_DoesNotContainBasePath() + { + // Arrange + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal($"Missing required metadata 'BasePath' for '{Path.Combine("wwwroot", "js", "sample.js")}'.", message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_DoesNotContainRelativePath() + { + // Arrange + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal($"Missing required metadata 'RelativePath' for '{Path.Combine("wwwroot", "js", "sample.js")}'.", message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_HaveDifferentSourceType() + { + // Arrange + var expectedError = "Static web assets have different 'SourceType' metadata values " + + "'' and 'Package' " + + $"for '{Path.Combine("wwwroot", "js", "sample.js")}' and '{Path.Combine("wwwroot", "css", "site.css")}'."; + + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }), + CreateItem(Path.Combine("wwwroot","css","site.css"), new Dictionary + { + ["SourceType"] = "Package", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("css", "site.css"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal(expectedError, message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_HaveDifferentSourceId() + { + // Arrange + var expectedError = "Static web assets have different 'SourceId' metadata values " + + "'MyLibrary' and 'MyLibrary2' " + + $"for '{Path.Combine("wwwroot", "js", "sample.js")}' and '{Path.Combine("wwwroot", "css", "site.css")}'."; + + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }), + CreateItem(Path.Combine("wwwroot","css","site.css"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary2", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("css", "site.css"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal(expectedError, message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_HaveDifferentContentRoot() + { + // Arrange + var expectedError = "Static web assets have different 'ContentRoot' metadata values " + + @"'$(MSBuildThisFileDirectory)..\staticwebassets' and '..\staticwebassets' " + + $"for '{Path.Combine("wwwroot", "js", "sample.js")}' and '{Path.Combine("wwwroot", "css", "site.css")}'."; + + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }), + CreateItem(Path.Combine("wwwroot","css","site.css"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("css", "site.css"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal(expectedError, message); + } + + [Fact] + public void Fails_WhenStaticWebAsset_HaveDifferentBasePath() + { + // Arrange + var expectedError = "Static web assets have different 'BasePath' metadata values " + + "'_content/mylibrary' and '_content/mylibrary2' " + + $"for '{Path.Combine("wwwroot", "js", "sample.js")}' and '{Path.Combine("wwwroot", "css", "site.css")}'."; + + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }), + CreateItem(Path.Combine("wwwroot","css","site.css"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary2", + ["RelativePath"] = Path.Combine("css", "site.css"), + }) + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.False(result); + var message = Assert.Single(errorMessages); + Assert.Equal(expectedError, message); + } + + [Fact] + public void WritesPropsFile_WhenThereIsAtLeastOneStaticAsset() + { + // Arrange + var file = Path.GetTempFileName(); + var expectedDocument = @" + + + Package + MyLibrary + $(MSBuildThisFileDirectory)..\staticwebassets\ + _content/mylibrary + %(RecursiveDir)%(FileName)%(Extension) + + +"; + + try + { + var buildEngine = new Mock(); + + var task = new GenerateStaticWebAsssetsPropsFile + { + BuildEngine = buildEngine.Object, + TargetPropsFilePath = file, + StaticWebAssets = new TaskItem[] + { + CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary + { + ["SourceType"] = "", + ["SourceId"] = "MyLibrary", + ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets", + ["BasePath"] = "_content/mylibrary", + ["RelativePath"] = Path.Combine("js", "sample.js"), + }), + } + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.True(result); + var document = File.ReadAllText(file); + Assert.Equal(expectedDocument, document); + } + finally + { + if (File.Exists(file)) + { + File.Delete(file); + } + } + } + + private static TaskItem CreateItem( + string spec, + IDictionary metadata) + { + var result = new TaskItem(spec); + foreach (var (key, value) in metadata) + { + result.SetMetadata(key, value); + } + + return result; + } + } +} diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs index bdbfa9c25d..3676f093e6 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs @@ -485,6 +485,40 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } + // This method extracts the nupkg to a fixed directory path. To avoid the extra work of + // cleaning up after each invocation, this method accepts multiple files. + public static void NupkgDoesNotContain(MSBuildResult result, string nupkgPath, params string[] filePaths) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + if (nupkgPath == null) + { + throw new ArgumentNullException(nameof(nupkgPath)); + } + + if (filePaths == null) + { + throw new ArgumentNullException(nameof(filePaths)); + } + + nupkgPath = Path.Combine(result.Project.DirectoryPath, nupkgPath); + FileExists(result, nupkgPath); + + var unzipped = Path.Combine(result.Project.DirectoryPath, Path.GetFileNameWithoutExtension(nupkgPath)); + ZipFile.ExtractToDirectory(nupkgPath, unzipped); + + foreach (var filePath in filePaths) + { + if (File.Exists(Path.Combine(unzipped, filePath))) + { + throw new NupkgFileFoundException(result, nupkgPath, filePath); + } + } + } + public static void AssemblyContainsType(MSBuildResult result, string assemblyPath, string fullTypeName) { if (result == null) @@ -872,5 +906,21 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests protected override string Heading => $"File: '{FilePath}' was not found was not found in {NupkgPath}."; } + + private class NupkgFileFoundException : MSBuildXunitException + { + public NupkgFileFoundException(MSBuildResult result, string nupkgPath, string filePath) + : base(result) + { + NupkgPath = nupkgPath; + FilePath = filePath; + } + + public string FilePath { get; } + + public string NupkgPath { get; } + + protected override string Heading => $"File: '{FilePath}' was found in {NupkgPath}."; + } } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/PackIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/PackIntegrationTest.cs index be0a44c78b..f1502e0a37 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/PackIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/PackIntegrationTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -176,5 +177,150 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Path.Combine("bin", Configuration, "ClassLibrary.1.0.0.nupkg"), Path.Combine("lib", "netcoreapp3.0", "ClassLibrary.Views.dll")); } + + [Fact] + [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })] + public async Task Pack_IncludesStaticWebAssets() + { + var result = await DotnetMSBuild("Pack"); + + Assert.BuildPassed(result, allowWarnings: true); + + Assert.FileExists(result, OutputPath, "PackageLibraryDirectDependency.dll"); + + Assert.NupkgContains( + result, + Path.Combine("..", "TestPackageRestoreSource", "PackageLibraryDirectDependency.1.0.0.nupkg"), + filePaths: new[] + { + Path.Combine("staticwebassets", "js", "pkg-direct-dep.js"), + Path.Combine("staticwebassets", "css", "site.css"), + Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"), + Path.Combine("build", "PackageLibraryDirectDependency.props"), + Path.Combine("buildMultiTargeting", "PackageLibraryDirectDependency.props"), + Path.Combine("buildTransitive", "PackageLibraryDirectDependency.props") + }); + } + + [Fact] + [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })] + public async Task Pack_StaticWebAssetsEnabledFalse_DoesNotPackAnyStaticWebAssets() + { + var result = await DotnetMSBuild("Pack", "/p:StaticWebAssetsEnabled=false"); + + Assert.BuildPassed(result, allowWarnings: true); + + Assert.FileExists(result, OutputPath, "PackageLibraryDirectDependency.dll"); + + Assert.NupkgDoesNotContain( + result, + Path.Combine("..", "TestPackageRestoreSource", "PackageLibraryDirectDependency.1.0.0.nupkg"), + filePaths: new[] + { + Path.Combine("staticwebassets", "js", "pkg-direct-dep.js"), + Path.Combine("staticwebassets", "css", "site.css"), + Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"), + Path.Combine("build", "PackageLibraryDirectDependency.props"), + Path.Combine("buildMultiTargeting", "PackageLibraryDirectDependency.props"), + Path.Combine("buildTransitive", "PackageLibraryDirectDependency.props") + }); + } + + [Fact] + [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })] + public async Task Pack_NoBuild_IncludesStaticWebAssets() + { + var result = await DotnetMSBuild("Build"); + Assert.BuildPassed(result, allowWarnings: true); + + var pack = await DotnetMSBuild("Pack", "/p:NoBuild=true"); + Assert.BuildPassed(pack, allowWarnings: true); + + Assert.FileExists(pack, OutputPath, "PackageLibraryDirectDependency.dll"); + + Assert.NupkgContains( + pack, + Path.Combine("..", "TestPackageRestoreSource", "PackageLibraryDirectDependency.1.0.0.nupkg"), + filePaths: new[] + { + Path.Combine("staticwebassets", "js", "pkg-direct-dep.js"), + Path.Combine("staticwebassets", "css", "site.css"), + Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"), + Path.Combine("build", "PackageLibraryDirectDependency.props"), + Path.Combine("buildMultiTargeting", "PackageLibraryDirectDependency.props"), + Path.Combine("buildTransitive", "PackageLibraryDirectDependency.props") + }); + } + + [Fact] + [InitializeTestProject("ComponentLibrary")] + public async Task Pack_DoesNotIncludeAnyCustomPropsFiles_WhenNoStaticAssetsAreAvailable() + { + MSBuildIntegrationTestBase.TargetFramework = "netstandard2.0"; + + var result = await DotnetMSBuild("Pack"); + + Assert.BuildPassed(result, allowWarnings: true); + + Assert.FileExists(result, OutputPath, "ComponentLibrary.dll"); + + Assert.NupkgDoesNotContain( + result, + Path.Combine("bin", Configuration, "ComponentLibrary.1.0.0.nupkg"), + filePaths: new[] + { + Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"), + Path.Combine("build", "ComponentLibrary.props"), + Path.Combine("buildMultiTargeting", "ComponentLibrary.props"), + Path.Combine("buildTransitive", "ComponentLibrary.props") + }); + } + + [Fact] + [InitializeTestProject("PackageLibraryTransitiveDependency")] + public async Task Pack_Incremental_DoesNotRegenerateCacheAndPropsFiles() + { + TargetFramework = "netstandard2.0"; + var result = await DotnetMSBuild("Pack"); + + Assert.BuildPassed(result, allowWarnings: true); + + Assert.FileExists(result, OutputPath, "PackageLibraryTransitiveDependency.dll"); + + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "msbuild.PackageLibraryTransitiveDependency.Microsoft.AspNetCore.StaticWebAssets.props"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "msbuild.build.PackageLibraryTransitiveDependency.props"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "msbuild.buildMultiTargeting.PackageLibraryTransitiveDependency.props"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "msbuild.buildTransitive.PackageLibraryTransitiveDependency.props"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "PackageLibraryTransitiveDependency.StaticWebAssets.Pack.cache"); + + var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath, "staticwebassets"); + var thumbPrints = new Dictionary(); + var thumbPrintFiles = new[] + { + Path.Combine(directoryPath, "msbuild.PackageLibraryTransitiveDependency.Microsoft.AspNetCore.StaticWebAssets.props"), + Path.Combine(directoryPath, "msbuild.build.PackageLibraryTransitiveDependency.props"), + Path.Combine(directoryPath, "msbuild.buildMultiTargeting.PackageLibraryTransitiveDependency.props"), + Path.Combine(directoryPath, "msbuild.buildTransitive.PackageLibraryTransitiveDependency.props"), + Path.Combine(directoryPath, "PackageLibraryTransitiveDependency.StaticWebAssets.Pack.cache"), + }; + + foreach (var file in thumbPrintFiles) + { + var thumbprint = GetThumbPrint(file); + thumbPrints[file] = thumbprint; + } + + // Act + var incremental = await DotnetMSBuild("Pack"); + + // Assert + Assert.BuildPassed(incremental, allowWarnings: true); + + foreach (var file in thumbPrintFiles) + { + var thumbprint = GetThumbPrint(file); + Assert.Equal(thumbPrints[file], thumbprint); + } + } } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/StaticWebAssetsIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/StaticWebAssetsIntegrationTest.cs index 653231d328..f8afa92f24 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/StaticWebAssetsIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/StaticWebAssetsIntegrationTest.cs @@ -43,18 +43,73 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.BuildPassed(result); // GenerateStaticWebAssetsManifest should generate the manifest and the cache. - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // Skip this check on mac as the CI seems to use a somewhat different path on OSX. + // This check works just fine on a local OSX instance, but the CI path seems to require prepending /private. + // There is nothing OS specific about publishing this file, so the chances of this breaking are infinitesimal. + Assert.FileExists(result, OutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + } var path = Assert.FileExists(result, OutputPath, "AppWithPackageAndP2PReference.dll"); var assembly = Assert.ContainsEmbeddedResource(path, "Microsoft.AspNetCore.StaticWebAssets.xml"); using (var reader = new StreamReader(assembly)) { var data = await reader.ReadToEndAsync(); + Output.WriteLine("Manifest:"); + Output.WriteLine(data); Assert.Equal(expectedManifest, data); } } + [Fact] + [InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })] + public async Task Publish_CopiesStaticWebAssetsToDestinationFolder() + { + var result = await DotnetMSBuild("Publish", "/restore"); + + Assert.BuildPassed(result); + + 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() + { + var build = await DotnetMSBuild("Build", "/restore"); + + Assert.BuildPassed(build); + + var publish = await DotnetMSBuild("Publish", "/p:BuildProjectReferences=false"); + + Assert.BuildPassed(publish); + + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "classlibrary", "js", "project-transitive-dep.js")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "classlibrary", "js", "project-transitive-dep.v4.js")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "classlibrary2", "css", "site.css")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "classlibrary2", "js", "project-direct-dep.js")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "packagelibrarydirectdependency", "css", "site.css")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "packagelibrarydirectdependency", "js", "pkg-direct-dep.js")); + Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "packagelibrarytransitivedependency", "js", "pkg-transitive-dep.js")); + } + + [Fact] [InitializeTestProject("SimpleMvc")] public async Task Build_DoesNotEmbedManifestWhen_NoStaticResourcesAvailable() @@ -64,8 +119,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.BuildPassed(result); // GenerateStaticWebAssetsManifest should generate the manifest and the cache. - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.StaticWebAssets.xml"); - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.StaticWebAssets.cache"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "SimpleMvc.StaticWebAssets.xml"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "SimpleMvc.StaticWebAssets.Manifest.cache"); + Assert.FileDoesNotExist(result, OutputPath, "SimpleMvc.StaticWebAssets.xml"); var path = Assert.FileExists(result, OutputPath, "SimpleMvc.dll"); Assert.DoesNotContainEmbeddedResource(path, "SimpleMvc.StaticWebAssets.xml"); @@ -80,16 +136,16 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.BuildPassed(result); // GenerateStaticWebAssetsManifest should generate the manifest and the cache. - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"); var cleanResult = await DotnetMSBuild("Clean"); Assert.BuildPassed(cleanResult); // Clean should delete the manifest and the cache. - Assert.FileDoesNotExist(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"); - Assert.FileDoesNotExist(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + Assert.FileDoesNotExist(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"); + Assert.FileDoesNotExist(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.xml"); } [Fact] @@ -104,15 +160,15 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.BuildPassed(result); // GenerateStaticWebAssetsManifest should generate the manifest and the cache. - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"); - var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath); + var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath, "staticwebassets"); var thumbPrints = new Dictionary(); var thumbPrintFiles = new[] { Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"), - Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"), + Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"), }; foreach (var file in thumbPrintFiles) @@ -151,15 +207,15 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.BuildPassed(result); // GenerateStaticWebAssetsManifest should generate the manifest and the cache. - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"); - Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.xml"); + Assert.FileExists(result, IntermediateOutputPath, "staticwebassets", "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"); - var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath); + var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath, "staticwebassets"); var thumbPrints = new Dictionary(); var thumbPrintFiles = new[] { Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"), - Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"), + Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.Manifest.cache"), }; foreach (var file in thumbPrintFiles) @@ -201,17 +257,17 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests var restorePath = LocalNugetPackagesCacheTempPath; var projects = new[] { - Path.Combine(restorePath, "packagelibrarytransitivedependency", "1.0.0", "buildTransitive", "..", "razorContent") + Path.DirectorySeparatorChar, - Path.Combine(restorePath, "packagelibrarydirectdependency", "1.0.0", "build", "..", "razorContent") + Path.DirectorySeparatorChar, + Path.Combine(restorePath, "packagelibrarytransitivedependency", "1.0.0", "build", "..", "staticwebassets") + Path.DirectorySeparatorChar, + Path.Combine(restorePath, "packagelibrarydirectdependency", "1.0.0", "build", "..", "staticwebassets") + Path.DirectorySeparatorChar, Path.GetFullPath(Path.Combine(source, "ClassLibrary", "wwwroot")) + Path.DirectorySeparatorChar, Path.GetFullPath(Path.Combine(source, "ClassLibrary2", "wwwroot")) + Path.DirectorySeparatorChar }; return $@" - - + + "; } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/StaticWebAssetsGeneratePackagePropsFileTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/StaticWebAssetsGeneratePackagePropsFileTest.cs new file mode 100644 index 0000000000..192462fb9b --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/StaticWebAssetsGeneratePackagePropsFileTest.cs @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.Build.Framework; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Tasks +{ + public class StaticWebAssetsGeneratePackagePropsFileTest + { + [Fact] + public void WritesPropsFile_WithProvidedImportPath() + { + // Arrange + var file = Path.GetTempFileName(); + var expectedDocument = @" + +"; + + try + { + var buildEngine = new Mock(); + + var task = new StaticWebAssetsGeneratePackagePropsFile + { + BuildEngine = buildEngine.Object, + PropsFileImport="Microsoft.AspNetCore.StaticWebAssets.props", + BuildTargetPath=file + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.True(result); + var document = File.ReadAllText(file); + Assert.Equal(expectedDocument, document); + } + finally + { + if (File.Exists(file)) + { + File.Delete(file); + } + } + } + } +} diff --git a/src/Razor/test/testassets/PackageLibraryDirectDependency/PackageLibraryDirectDependency.csproj b/src/Razor/test/testassets/PackageLibraryDirectDependency/PackageLibraryDirectDependency.csproj index 075a8e6852..eacc72651e 100644 --- a/src/Razor/test/testassets/PackageLibraryDirectDependency/PackageLibraryDirectDependency.csproj +++ b/src/Razor/test/testassets/PackageLibraryDirectDependency/PackageLibraryDirectDependency.csproj @@ -36,9 +36,9 @@ - + - + diff --git a/src/Razor/test/testassets/PackageLibraryDirectDependency/build/PackageLibraryDirectDependency.props b/src/Razor/test/testassets/PackageLibraryDirectDependency/build/PackageLibraryDirectDependency.props deleted file mode 100644 index eaa14f2c3a..0000000000 --- a/src/Razor/test/testassets/PackageLibraryDirectDependency/build/PackageLibraryDirectDependency.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - Package - PackageLibraryDirectDependency - $([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)..\razorContent')) - _content\PackageLibraryDirectDependency - %(RecursiveDir)%(FileName)%(Extension) - - - \ No newline at end of file diff --git a/src/Razor/test/testassets/PackageLibraryTransitiveDependency/PackageLibraryTransitiveDependency.csproj b/src/Razor/test/testassets/PackageLibraryTransitiveDependency/PackageLibraryTransitiveDependency.csproj index e4ac49a002..a83ad7086e 100644 --- a/src/Razor/test/testassets/PackageLibraryTransitiveDependency/PackageLibraryTransitiveDependency.csproj +++ b/src/Razor/test/testassets/PackageLibraryTransitiveDependency/PackageLibraryTransitiveDependency.csproj @@ -6,6 +6,7 @@ true + 3.0 @@ -35,10 +36,4 @@ - - - - - - diff --git a/src/Razor/test/testassets/PackageLibraryTransitiveDependency/build/PackageLibraryTransitiveDependency.props b/src/Razor/test/testassets/PackageLibraryTransitiveDependency/build/PackageLibraryTransitiveDependency.props deleted file mode 100644 index 64cdf1337b..0000000000 --- a/src/Razor/test/testassets/PackageLibraryTransitiveDependency/build/PackageLibraryTransitiveDependency.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - Package - PackageLibraryTransitiveDependency - $([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)..\razorContent')) - _content\PackageLibraryTransitiveDependency - %(RecursiveDir)%(FileName)%(Extension) - - - \ No newline at end of file