Skip to content

Commit 87817be

Browse files
authored
Adds support for packing and publishing static web assets
* Adds support for publishing static web assets * At publish time, it copies all the referenced assets from referenced projects and packages into their final locations. * Automatically pack static web assets for consumption * Generate Microsoft.AspNetCore.StaticWebAssets.props and pack it into build\Microsoft.AspNetCore.StaticWebAssets.props * Generate `<<PackageId>>.props` and pack it into `build\<<PackageId>>.props` importing Microsoft.AspNetCore.StaticWebAssets.props * Generate `<<PackageId>>.props` and pack it into buildMultiTargeting\`<<PackageId>>.props` importing `build\<<PackageId>>.props` * Generate `<<PackageId>>.props` and pack it into buildTransitive\`<<PackageId>>.props` importing buildMultiTargeting\`<<PackageId>>.props` * Pack all the static web assets from the current project into `staticwebassets\**`
1 parent c205871 commit 87817be

15 files changed

+1284
-74
lines changed

src/Razor/src/Microsoft.NET.Sdk.Razor/GenerateStaticWebAssetsManifest.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7+
using System.Linq;
78
using System.Text;
89
using System.Xml;
910
using System.Xml.Linq;
@@ -85,7 +86,8 @@ private IEnumerable<XElement> CreateNodes()
8586
}
8687
}
8788

88-
return nodes;
89+
// Its important that we order the nodes here to produce a manifest deterministically.
90+
return nodes.OrderBy(e=>e.Attribute(BasePath).Value);
8991
}
9092

9193
private XmlWriter GetXmlWriter(XmlWriterSettings settings)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Text;
7+
using System.Xml;
8+
using System.Xml.Linq;
9+
using Microsoft.Build.Framework;
10+
using Microsoft.Build.Utilities;
11+
12+
namespace Microsoft.AspNetCore.Razor.Tasks
13+
{
14+
public class GenerateStaticWebAsssetsPropsFile : Task
15+
{
16+
private const string SourceType = "SourceType";
17+
private const string SourceId = "SourceId";
18+
private const string ContentRoot = "ContentRoot";
19+
private const string BasePath = "BasePath";
20+
private const string RelativePath = "RelativePath";
21+
22+
[Required]
23+
public string TargetPropsFilePath { get; set; }
24+
25+
[Required]
26+
public ITaskItem[] StaticWebAssets { get; set; }
27+
28+
public override bool Execute()
29+
{
30+
if (!ValidateArguments())
31+
{
32+
return false;
33+
}
34+
35+
return ExecuteCore();
36+
}
37+
38+
private bool ExecuteCore()
39+
{
40+
if (StaticWebAssets.Length == 0)
41+
{
42+
return !Log.HasLoggedErrors;
43+
}
44+
45+
var template = StaticWebAssets[0];
46+
47+
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
48+
var root = new XElement(
49+
"Project",
50+
new XElement("ItemGroup",
51+
new XElement("StaticWebAsset",
52+
new XAttribute("Include", @"$(MSBuildThisFileDirectory)..\staticwebassets\**"),
53+
new XElement(SourceType, "Package"),
54+
new XElement(SourceId, template.GetMetadata(SourceId)),
55+
new XElement(ContentRoot, @"$(MSBuildThisFileDirectory)..\staticwebassets\"),
56+
new XElement(BasePath, template.GetMetadata(BasePath)),
57+
new XElement(RelativePath, "%(RecursiveDir)%(FileName)%(Extension)"))));
58+
59+
document.Add(root);
60+
61+
var settings = new XmlWriterSettings
62+
{
63+
Encoding = Encoding.UTF8,
64+
CloseOutput = true,
65+
OmitXmlDeclaration = true,
66+
Indent = true,
67+
NewLineOnAttributes = false,
68+
Async = true
69+
};
70+
71+
using (var xmlWriter = GetXmlWriter(settings))
72+
{
73+
document.WriteTo(xmlWriter);
74+
}
75+
76+
return !Log.HasLoggedErrors;
77+
}
78+
79+
private XmlWriter GetXmlWriter(XmlWriterSettings settings)
80+
{
81+
var fileStream = new FileStream(TargetPropsFilePath, FileMode.Create);
82+
return XmlWriter.Create(fileStream, settings);
83+
}
84+
85+
private bool ValidateArguments()
86+
{
87+
ITaskItem firstAsset = null;
88+
89+
for (var i = 0; i < StaticWebAssets.Length; i++)
90+
{
91+
var webAsset = StaticWebAssets[i];
92+
if (!EnsureRequiredMetadata(webAsset, SourceId) ||
93+
!EnsureRequiredMetadata(webAsset, SourceType, allowEmpty: true) ||
94+
!EnsureRequiredMetadata(webAsset, ContentRoot) ||
95+
!EnsureRequiredMetadata(webAsset, BasePath) ||
96+
!EnsureRequiredMetadata(webAsset, RelativePath))
97+
{
98+
return false;
99+
}
100+
101+
if (firstAsset == null)
102+
{
103+
firstAsset = webAsset;
104+
continue;
105+
}
106+
107+
if (!ValidateMetadataMatches(firstAsset, webAsset, SourceId) ||
108+
!ValidateMetadataMatches(firstAsset, webAsset, SourceType) ||
109+
!ValidateMetadataMatches(firstAsset, webAsset, ContentRoot) ||
110+
!ValidateMetadataMatches(firstAsset, webAsset, BasePath))
111+
{
112+
return false;
113+
}
114+
}
115+
116+
return true;
117+
}
118+
119+
private bool ValidateMetadataMatches(ITaskItem reference, ITaskItem candidate, string metadata)
120+
{
121+
var referenceMetadata = reference.GetMetadata(metadata);
122+
var candidateMetadata = candidate.GetMetadata(metadata);
123+
if (!string.Equals(referenceMetadata, candidateMetadata, System.StringComparison.Ordinal))
124+
{
125+
Log.LogError($"Static web assets have different '{metadata}' metadata values '{referenceMetadata}' and '{candidateMetadata}' for '{reference.ItemSpec}' and '{candidate.ItemSpec}'.");
126+
return false;
127+
}
128+
129+
return true;
130+
}
131+
132+
private bool EnsureRequiredMetadata(ITaskItem item, string metadataName, bool allowEmpty = false)
133+
{
134+
var value = item.GetMetadata(metadataName);
135+
var isInvalidValue = allowEmpty ? !HasMetadata(item, metadataName) : string.IsNullOrEmpty(value);
136+
137+
if (isInvalidValue)
138+
{
139+
Log.LogError($"Missing required metadata '{metadataName}' for '{item.ItemSpec}'.");
140+
return false;
141+
}
142+
143+
return true;
144+
}
145+
146+
private bool HasMetadata(ITaskItem item, string metadataName)
147+
{
148+
foreach (var name in item.MetadataNames)
149+
{
150+
if (string.Equals(metadataName, (string)name, StringComparison.Ordinal))
151+
{
152+
return true;
153+
}
154+
}
155+
156+
return false;
157+
}
158+
}
159+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System.IO;
4+
using System.Text;
5+
using System.Xml;
6+
using System.Xml.Linq;
7+
using Microsoft.Build.Framework;
8+
using Microsoft.Build.Utilities;
9+
10+
namespace Microsoft.AspNetCore.Razor.Tasks
11+
{
12+
public class StaticWebAssetsGeneratePackagePropsFile : Task
13+
{
14+
[Required]
15+
public string PropsFileImport { get; set; }
16+
17+
[Required]
18+
public string BuildTargetPath { get; set; }
19+
20+
public override bool Execute()
21+
{
22+
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
23+
var root = new XElement(
24+
"Project",
25+
new XElement("Import",
26+
new XAttribute("Project", PropsFileImport)));
27+
28+
document.Add(root);
29+
30+
var settings = new XmlWriterSettings
31+
{
32+
Encoding = Encoding.UTF8,
33+
CloseOutput = true,
34+
OmitXmlDeclaration = true,
35+
Indent = true,
36+
NewLineOnAttributes = false,
37+
Async = true
38+
};
39+
40+
using (var xmlWriter = GetXmlWriter(settings))
41+
{
42+
document.WriteTo(xmlWriter);
43+
}
44+
45+
return !Log.HasLoggedErrors;
46+
}
47+
48+
private XmlWriter GetXmlWriter(XmlWriterSettings settings)
49+
{
50+
var fileStream = new FileStream(BuildTargetPath, FileMode.Create);
51+
return XmlWriter.Create(fileStream, settings);
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)