Skip to content

Commit f01e8ae

Browse files
authored
[Static web assets] Add support for filtering the final list of endpoints (#50292)
Allows filtering the list of endpoints at build/publish time to limit the number of endpoints mapped by MapStaticAssets via the property StaticWebAssetEndpointExclusionPattern
1 parent cff6f49 commit f01e8ae

File tree

4 files changed

+324
-8
lines changed

4 files changed

+324
-8
lines changed

src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Publish.targets

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@ Copyright (c) .NET Foundation. All rights reserved.
3939
ManifestType="Publish"
4040
Assets="@(_FinalPublishStaticWebAsset)"
4141
Endpoints="@(StaticWebAssetEndpoint)"
42-
ManifestPath="$(StaticWebAssetEndpointsPublishManifestPath)">
42+
ManifestPath="$(StaticWebAssetEndpointsPublishManifestPath)"
43+
ExclusionPatterns="$(StaticWebAssetEndpointExclusionPattern)"
44+
ExclusionPatternsCacheFilePath="$(StaticWebAssetEndpointsPublishExclusionPatternsCachePath)">
4345
</GenerateStaticWebAssetEndpointsManifest>
4446

4547
<ItemGroup>
4648
<FileWrites Include="$(StaticWebAssetPublishManifestPath)" />
4749
<FileWrites Include="$(StaticWebAssetEndpointsPublishManifestPath)" />
50+
<FileWrites Include="$(StaticWebAssetEndpointsPublishExclusionPatternsCachePath)" Condition="Exists('$(StaticWebAssetEndpointsPublishExclusionPatternsCachePath)')" />
4851
</ItemGroup>
4952
</Target>
5053

src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ Copyright (c) .NET Foundation. All rights reserved.
443443
<StaticWebAssetDevelopmentManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.development.json</StaticWebAssetDevelopmentManifestPath>
444444
<StaticWebAssetEndpointsBuildManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.build.endpoints.json</StaticWebAssetEndpointsBuildManifestPath>
445445
<StaticWebAssetEndpointsPublishManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.publish.endpoints.json</StaticWebAssetEndpointsPublishManifestPath>
446+
<StaticWebAssetEndpointsBuildExclusionPatternsCachePath>$(_StaticWebAssetsManifestBase)swae.build.ex.cache</StaticWebAssetEndpointsBuildExclusionPatternsCachePath>
447+
<StaticWebAssetEndpointsPublishExclusionPatternsCachePath>$(_StaticWebAssetsManifestBase)swae.publish.ex.cache</StaticWebAssetEndpointsPublishExclusionPatternsCachePath>
446448
<StaticWebAssetPublishManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.publish.json</StaticWebAssetPublishManifestPath>
447449

448450
<_StaticWebAssetsIntermediateOutputPath>$(IntermediateOutputPath)staticwebassets\</_StaticWebAssetsIntermediateOutputPath>
@@ -632,7 +634,9 @@ Copyright (c) .NET Foundation. All rights reserved.
632634
Assets="@(StaticWebAsset)"
633635
Endpoints="@(StaticWebAssetEndpoint)"
634636
ManifestPath="$(StaticWebAssetEndpointsBuildManifestPath)"
635-
CacheFilePath="$(StaticWebAssetBuildManifestPath)">
637+
CacheFilePath="$(StaticWebAssetBuildManifestPath)"
638+
ExclusionPatterns="$(StaticWebAssetEndpointExclusionPattern)"
639+
ExclusionPatternsCacheFilePath="$(StaticWebAssetEndpointsBuildExclusionPatternsCachePath)">
636640
</GenerateStaticWebAssetEndpointsManifest>
637641

638642
<GenerateStaticWebAssetsDevelopmentManifest
@@ -655,6 +659,7 @@ Copyright (c) .NET Foundation. All rights reserved.
655659
<FileWrites Include="$(StaticWebAssetsBuildManifestCacheFilePath)" />
656660
<FileWrites Include="$(StaticWebAssetDevelopmentManifestPath)" />
657661
<FileWrites Include="$(StaticWebAssetEndpointsBuildManifestPath)" />
662+
<FileWrites Include="$(StaticWebAssetEndpointsBuildExclusionPatternsCachePath)" Condition="Exists('$(StaticWebAssetEndpointsBuildExclusionPatternsCachePath)')" />
658663
</ItemGroup>
659664

660665
</Target>

src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,60 @@ public class GenerateStaticWebAssetEndpointsManifest : Task
2626

2727
public string CacheFilePath { get; set; }
2828

29+
public string ExclusionPatterns { get; set; }
30+
31+
public string ExclusionPatternsCacheFilePath { get; set; }
32+
2933
public override bool Execute()
3034
{
35+
var (patternString, parsedPatterns) = ParseAndSortPatterns(ExclusionPatterns);
36+
var existingPatternString = !string.IsNullOrEmpty(ExclusionPatternsCacheFilePath) && File.Exists(ExclusionPatternsCacheFilePath)
37+
? File.ReadAllText(ExclusionPatternsCacheFilePath)
38+
: null;
39+
existingPatternString = string.IsNullOrEmpty(existingPatternString) ? null : existingPatternString;
3140
if (!string.IsNullOrEmpty(CacheFilePath) && File.Exists(ManifestPath) && File.GetLastWriteTimeUtc(ManifestPath) > File.GetLastWriteTimeUtc(CacheFilePath))
3241
{
33-
Log.LogMessage(MessageImportance.Low, "Skipping manifest generation because manifest file '{0}' is up to date.", ManifestPath);
34-
return true;
42+
// Check if exclusion patterns cache is also up to date
43+
if (string.Equals(patternString, existingPatternString, StringComparison.Ordinal))
44+
{
45+
Log.LogMessage(MessageImportance.Low, "Skipping manifest generation because manifest file '{0}' is up to date.", ManifestPath);
46+
return true;
47+
}
48+
else
49+
{
50+
Log.LogMessage(MessageImportance.Low, "Generating manifest file '{0}' because exclusion patterns changed from '{1}' to '{2}'.", ManifestPath,
51+
existingPatternString ?? "no patterns",
52+
patternString ?? "no patterns");
53+
}
54+
}
55+
else
56+
{
57+
Log.LogMessage(MessageImportance.Low, "Generating manifest file '{0}' because manifest file is missing or out of date.", ManifestPath);
3558
}
3659

3760
try
3861
{
62+
// Update exclusion patterns cache if needed
63+
UpdateExclusionPatternsCache(existingPatternString, patternString);
64+
3965
// Get the list of the asset that need to be part of the manifest (this is similar to GenerateStaticWebAssetsDevelopmentManifest)
4066
var assets = StaticWebAsset.FromTaskItemGroup(Assets);
4167
var manifestAssets = ComputeManifestAssets(assets, ManifestType)
4268
.ToDictionary(a => a.ResolvedAsset.Identity, a => a, OSPath.PathComparer);
4369

70+
// Build exclusion matcher if patterns are provided
71+
StaticWebAssetGlobMatcher exclusionMatcher = null;
72+
if (parsedPatterns.Length > 0)
73+
{
74+
var builder = new StaticWebAssetGlobMatcherBuilder();
75+
builder.AddIncludePatternsList(parsedPatterns);
76+
exclusionMatcher = builder.Build();
77+
}
78+
4479
// Filter out the endpoints to those that point to the assets that are part of the manifest
4580
var endpoints = StaticWebAssetEndpoint.FromItemGroup(Endpoints);
4681
var filteredEndpoints = new List<StaticWebAssetEndpoint>();
47-
82+
var updatedManifest = false;
4883
foreach (var endpoint in endpoints)
4984
{
5085
if (!manifestAssets.TryGetValue(endpoint.AssetFile, out var asset))
@@ -53,14 +88,35 @@ public override bool Execute()
5388
continue;
5489
}
5590

91+
// Check if endpoint should be excluded based on patterns
92+
var route = asset.ResolvedAsset.ReplaceTokens(endpoint.Route, StaticWebAssetTokenResolver.Instance);
93+
if (exclusionMatcher != null)
94+
{
95+
var match = exclusionMatcher.Match(route);
96+
if (match.IsMatch)
97+
{
98+
if (!updatedManifest && File.Exists(ManifestPath))
99+
{
100+
updatedManifest = true;
101+
// Touch the manifest if we are excluding endpoints to ensure we don't keep reporting out of date
102+
// for the excluded endpoints.
103+
// (The SWA manifest we use as cache might get updated, but if we filter out the new endpoints, we won't
104+
// update the endpoints manifest file and on the next build we will re-enter this loop).
105+
Log.LogMessage(MessageImportance.Low, "Updating manifest timestamp '{0}'.", ManifestPath);
106+
File.SetLastWriteTime(ManifestPath, DateTime.UtcNow);
107+
}
108+
Log.LogMessage(MessageImportance.Low, "Excluding endpoint '{0}' based on exclusion patterns", route);
109+
continue;
110+
}
111+
}
112+
56113
filteredEndpoints.Add(endpoint);
57114
// Update the endpoint to use the target path of the asset, this will be relative to the wwwroot
58-
var path = endpoint.AssetFile;
59115

60116
endpoint.AssetFile = asset.ResolvedAsset.ComputeTargetPath("", '/', StaticWebAssetTokenResolver.Instance);
61-
endpoint.Route = asset.ResolvedAsset.ReplaceTokens(endpoint.Route, StaticWebAssetTokenResolver.Instance);
117+
endpoint.Route = route;
62118

63-
Log.LogMessage(MessageImportance.Low, "Including endpoint '{0}' for asset '{1}' with final location '{2}'", endpoint.Route, path, asset.TargetPath);
119+
Log.LogMessage(MessageImportance.Low, "Including endpoint '{0}' for asset '{1}' with final location '{2}'", endpoint.Route, endpoint.AssetFile, asset.TargetPath);
64120
}
65121

66122
var manifest = new StaticWebAssetEndpointsManifest()
@@ -81,6 +137,44 @@ public override bool Execute()
81137
return !Log.HasLoggedErrors;
82138
}
83139

140+
private static (string, string[]) ParseAndSortPatterns(string patterns)
141+
{
142+
if (string.IsNullOrEmpty(patterns))
143+
{
144+
return (null, []);
145+
}
146+
147+
var parsed = patterns.Split([';'], StringSplitOptions.RemoveEmptyEntries);
148+
Array.Sort(parsed, StringComparer.OrdinalIgnoreCase);
149+
150+
return (string.Join(Environment.NewLine, parsed), parsed);
151+
}
152+
153+
private void UpdateExclusionPatternsCache(string existingPatternString, string patternString)
154+
{
155+
if (string.IsNullOrEmpty(ExclusionPatternsCacheFilePath))
156+
{
157+
return;
158+
}
159+
160+
if (!File.Exists(ExclusionPatternsCacheFilePath) ||
161+
!string.Equals(existingPatternString, patternString, StringComparison.Ordinal))
162+
{
163+
var directoryName = Path.GetDirectoryName(ExclusionPatternsCacheFilePath);
164+
if (directoryName != null)
165+
{
166+
Directory.CreateDirectory(directoryName);
167+
}
168+
File.WriteAllText(ExclusionPatternsCacheFilePath, patternString);
169+
// We need to touch the file because otherwise we will keep thinking that is out of date in the future.
170+
// This file might not be rewritten if the results are unchanged.
171+
if (File.Exists(ManifestPath))
172+
{
173+
File.SetLastWriteTime(ManifestPath, DateTime.UtcNow);
174+
}
175+
}
176+
}
177+
84178
private IEnumerable<TargetPathAssetPair> ComputeManifestAssets(IEnumerable<StaticWebAsset> assets, string kind)
85179
{
86180
var assetsByTargetPath = assets

0 commit comments

Comments
 (0)