[Fixes #2841] Support comma separated globbed include and exclude pattern in Script and Link tag helpers
This commit is contained in:
parent
ed4896d1fd
commit
fcad4c5c57
|
|
@ -20,6 +20,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
{
|
||||
private static readonly char[] PatternSeparator = new[] { ',' };
|
||||
|
||||
// Valid whitespace characters defined by the HTML5 spec.
|
||||
private static readonly char[] ValidAttributeWhitespaceChars =
|
||||
new[] { '\t', '\n', '\u000C', '\r', ' ' };
|
||||
|
||||
private static readonly PathComparer DefaultPathComparer = new PathComparer();
|
||||
|
||||
private readonly FileProviderGlobbingDirectory _baseGlobbingDirectory;
|
||||
|
|
@ -125,11 +129,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
{
|
||||
var matcher = MatcherBuilder != null ? MatcherBuilder() : new Matcher();
|
||||
|
||||
matcher.AddIncludePatterns(includePatterns.Select(pattern => TrimLeadingSlash(pattern)));
|
||||
matcher.AddIncludePatterns(includePatterns.Select(pattern => TrimLeadingTildeSlash(pattern)));
|
||||
|
||||
if (excludePatterns != null)
|
||||
{
|
||||
matcher.AddExcludePatterns(excludePatterns.Select(pattern => TrimLeadingSlash(pattern)));
|
||||
matcher.AddExcludePatterns(excludePatterns.Select(pattern => TrimLeadingTildeSlash(pattern)));
|
||||
}
|
||||
|
||||
var matches = matcher.Execute(_baseGlobbingDirectory);
|
||||
|
|
@ -210,11 +214,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static string TrimLeadingSlash(string value)
|
||||
private static string TrimLeadingTildeSlash(string value)
|
||||
{
|
||||
var result = value;
|
||||
var result = value.Trim(ValidAttributeWhitespaceChars);
|
||||
|
||||
if (result.StartsWith("/", StringComparison.Ordinal) ||
|
||||
if (result.StartsWith("~/", StringComparison.Ordinal))
|
||||
{
|
||||
result = result.Substring(2);
|
||||
}
|
||||
else if (result.StartsWith("/", StringComparison.Ordinal) ||
|
||||
result.StartsWith("\\", StringComparison.Ordinal))
|
||||
{
|
||||
// Trim the leading slash as the matcher runs from the provided root only anyway
|
||||
|
|
|
|||
|
|
@ -253,15 +253,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
if (mode == Mode.GlobbedHref || mode == Mode.Fallback && !string.IsNullOrEmpty(HrefInclude))
|
||||
{
|
||||
if (TryResolveUrl(HrefInclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
HrefInclude = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(HrefExclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
HrefExclude = resolvedUrl;
|
||||
}
|
||||
|
||||
BuildGlobbedLinkTags(attributes, builder);
|
||||
if (string.IsNullOrEmpty(Href))
|
||||
{
|
||||
|
|
@ -277,14 +268,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
FallbackHref = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(FallbackHrefInclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
FallbackHrefInclude = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(FallbackHrefExclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
FallbackHrefExclude = resolvedUrl;
|
||||
}
|
||||
|
||||
BuildFallbackBlock(builder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,15 +220,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
if (mode == Mode.GlobbedSrc || mode == Mode.Fallback && !string.IsNullOrEmpty(SrcInclude))
|
||||
{
|
||||
if (TryResolveUrl(SrcInclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
SrcInclude = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(SrcExclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
SrcExclude = resolvedUrl;
|
||||
}
|
||||
|
||||
BuildGlobbedScriptTags(attributes, builder);
|
||||
if (string.IsNullOrEmpty(Src))
|
||||
{
|
||||
|
|
@ -244,14 +235,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
FallbackSrc = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(FallbackSrcInclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
FallbackSrcInclude = resolvedUrl;
|
||||
}
|
||||
if (TryResolveUrl(FallbackSrcExclude, encodeWebRoot: false, resolvedUrl: out resolvedUrl))
|
||||
{
|
||||
FallbackSrcExclude = resolvedUrl;
|
||||
}
|
||||
|
||||
BuildFallbackBlock(attributes, builder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
<!-- Globbed link tag with existing file -->
|
||||
<link rel="stylesheet" title="<the title>" href="HtmlEncode[[/site.css]]" />
|
||||
|
||||
<!-- Globbed link tag with comma separated pattern -->
|
||||
<link rel="stylesheet" title="<the title" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" title="<the title" href="HtmlEncode[[/sub/site2.css]]" /><link rel="stylesheet" title="<the title" href="HtmlEncode[[/sub/site3.css]]" /><link rel="stylesheet" title="<the title" href="HtmlEncode[[/sub/site3.min.css]]" />
|
||||
|
||||
<!-- Globbed link tag with existing file and exclude -->
|
||||
<link rel="stylesheet" title=""the" title" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" title=""the" title" href="HtmlEncode[[/sub/site2.css]]" />
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
<!-- Globbed link tag with existing file -->
|
||||
<link rel="stylesheet" title="<the title>" href="/site.css" />
|
||||
|
||||
<!-- Globbed link tag with comma separated pattern -->
|
||||
<link rel="stylesheet" title="<the title" href="/site.css" /><link rel="stylesheet" title="<the title" href="/sub/site2.css" /><link rel="stylesheet" title="<the title" href="/sub/site3.css" /><link rel="stylesheet" title="<the title" href="/sub/site3.min.css" />
|
||||
|
||||
<!-- Globbed link tag with existing file and exclude -->
|
||||
<link rel="stylesheet" title=""the" title" href="/site.css" /><link rel="stylesheet" title=""the" title" href="/sub/site2.css" />
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@
|
|||
// Globbed script tag missing include but with static src
|
||||
</script>
|
||||
|
||||
<!-- Globbed script tag with comma separated include pattern -->
|
||||
<script src="HtmlEncode[[/site.js]]"></script><script src="HtmlEncode[[/sub/site2.js]]"></script><script src="HtmlEncode[[/sub/site3.js]]"></script>
|
||||
|
||||
<!-- Globbed script tag with missing file -->
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@
|
|||
// Globbed script tag missing include but with static src
|
||||
</script>
|
||||
|
||||
<!-- Globbed script tag with comma separated include pattern -->
|
||||
<script src="/site.js"></script><script src="/sub/site2.js"></script><script src="/sub/site3.js"></script>
|
||||
|
||||
<!-- Globbed script tag with missing file -->
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
{
|
||||
/* staticUrl */ "/site.css",
|
||||
/* dirStructure */ new FileNode(null, new [] {
|
||||
|
||||
new FileNode("A", new [] {
|
||||
new FileNode("c.css"),
|
||||
new FileNode("d.css")
|
||||
|
|
@ -299,10 +298,68 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
Mock.Get(cache).VerifyAll();
|
||||
}
|
||||
|
||||
public static TheoryData CommaSeparatedPatternData
|
||||
{
|
||||
get
|
||||
{
|
||||
// Include pattern, expected output
|
||||
return new TheoryData<string, string[]>
|
||||
{
|
||||
{
|
||||
"~/*.css, ~/*.txt",
|
||||
new[] { "/site.css", "/site2.txt" }
|
||||
},
|
||||
{
|
||||
"*.css, /*.txt",
|
||||
new[] { "/site.css", "/site2.txt" }
|
||||
},
|
||||
{
|
||||
"\\*.css,~/*.txt",
|
||||
new[] { "/site.css", "/site2.txt" }
|
||||
},
|
||||
{
|
||||
"~/*.js, *.txt",
|
||||
new[] { "/blank.js", "/site.js", "/site2.txt" }
|
||||
},
|
||||
{
|
||||
" ~/*.js,*.txt, /*.css",
|
||||
new[] { "/blank.js", "/site.css", "/site.js", "/site2.txt" }
|
||||
},
|
||||
{
|
||||
"~/blank.js, blank.js,/blank.js, \\blank.js",
|
||||
new[] { "/blank.js" }
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(CommaSeparatedPatternData))]
|
||||
public void HandlesCommaSeparatedPatterns(string includePattern, string[] expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var fileProvider = MakeFileProvider(MakeDirectoryContents("site.css", "blank.js", "site2.txt", "site.js"));
|
||||
IMemoryCache cache = null;
|
||||
var requestPathBase = PathString.Empty;
|
||||
var globbingUrlBuilder = new GlobbingUrlBuilder(fileProvider, cache, requestPathBase);
|
||||
|
||||
// Act
|
||||
var urlList = globbingUrlBuilder.BuildUrlList(
|
||||
staticUrl: null,
|
||||
includePattern: includePattern,
|
||||
excludePattern: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedOutput, urlList, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("/")]
|
||||
[InlineData("\\")]
|
||||
public void TrimsLeadingSlashFromPatterns(string leadingSlash)
|
||||
[InlineData(" \\")]
|
||||
[InlineData("~/")]
|
||||
[InlineData(" ~/")]
|
||||
public void TrimsLeadingTildeAndSlashFromPatterns(string prefix)
|
||||
{
|
||||
// Arrange
|
||||
var fileProvider = MakeFileProvider(MakeDirectoryContents("site.css", "blank.css"));
|
||||
|
|
@ -317,8 +374,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
// Act
|
||||
var urlList = globbingUrlBuilder.BuildUrlList(
|
||||
staticUrl: null,
|
||||
includePattern: $"{leadingSlash}**/*.css",
|
||||
excludePattern: $"{leadingSlash}**/*.min.css");
|
||||
includePattern: $"{prefix}**/*.css",
|
||||
excludePattern: $"{prefix}**/*.min.css");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(includePatterns, pattern => Assert.Equal("**/*.css", pattern));
|
||||
|
|
@ -326,12 +383,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("~/")]
|
||||
[InlineData("/")]
|
||||
[InlineData("\\")]
|
||||
public void TrimsOnlySingleLeadingSlashFromPatterns(string leadingSlash)
|
||||
public void TrimsOnlySingleLeadingSlashOrTildeSlashFromPatterns(string prefix)
|
||||
{
|
||||
// Arrange
|
||||
var leadingSlashes = $"{leadingSlash}{leadingSlash}";
|
||||
var leadingSlashes = $"{prefix}{prefix}";
|
||||
var fileProvider = MakeFileProvider(MakeDirectoryContents("site.css", "blank.css"));
|
||||
IMemoryCache cache = null;
|
||||
var requestPathBase = PathString.Empty;
|
||||
|
|
@ -348,8 +406,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
excludePattern: $"{leadingSlashes}**/*.min.css");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(includePatterns, pattern => Assert.Equal($"{leadingSlash}**/*.css", pattern));
|
||||
Assert.Collection(excludePatterns, pattern => Assert.Equal($"{leadingSlash}**/*.min.css", pattern));
|
||||
Assert.Collection(includePatterns, pattern => Assert.Equal($"{prefix}**/*.css", pattern));
|
||||
Assert.Collection(excludePatterns, pattern => Assert.Equal($"{prefix}**/*.min.css", pattern));
|
||||
}
|
||||
|
||||
public class FileNode
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@
|
|||
<!-- Globbed link tag with existing file -->
|
||||
<link asp-href-include="**/site.css" rel="stylesheet" title="<the title>" />
|
||||
|
||||
<!-- Globbed link tag with comma separated pattern -->
|
||||
<link asp-href-include="~/*.css, ~/sub/*.css" rel="stylesheet" title="<the title" />
|
||||
|
||||
<!-- Globbed link tag with existing file and exclude -->
|
||||
<link asp-href-include="**/*.css" asp-href-exclude="**/site3*.css" rel="stylesheet" title='"the" title' />
|
||||
<link asp-href-include=" **/*.css" asp-href-exclude="**/site3*.css" rel="stylesheet" title='"the" title' />
|
||||
|
||||
<!-- Globbed link tag missing include -->
|
||||
<link asp-href-exclude="**/site2.css" rel="stylesheet" />
|
||||
|
|
@ -51,7 +54,7 @@
|
|||
asp-fallback-test-value="hidden" />
|
||||
|
||||
<!-- Fallback from globbed href with exclude to static href -->
|
||||
<link asp-href-include="**/*.min.css" asp-href-exclude="**/site3.min.css" rel="stylesheet" data-extra="test"
|
||||
<link asp-href-include="**/*.min.css" asp-href-exclude=" **/site3.min.css" rel="stylesheet" data-extra="test"
|
||||
asp-fallback-href="~/site.css"
|
||||
asp-fallback-test-class="hidden"
|
||||
asp-fallback-test-property="visibility"
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@
|
|||
// Globbed script tag missing include but with static src
|
||||
</script>
|
||||
|
||||
<!-- Globbed script tag with comma separated include pattern -->
|
||||
<script asp-src-include="~/*.js, ~/sub/*.js"></script>
|
||||
|
||||
<!-- Globbed script tag with missing file -->
|
||||
<script asp-src-include="**/notThere.js"></script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue