[Fixes #2841] Support comma separated globbed include and exclude pattern in Script and Link tag helpers

This commit is contained in:
Ajay Bhargav Baaskaran 2015-08-04 15:52:23 -07:00
parent ed4896d1fd
commit fcad4c5c57
10 changed files with 100 additions and 50 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -10,6 +10,9 @@
<!-- Globbed link tag with existing file -->
<link rel="stylesheet" title="&lt;the title>" href="HtmlEncode[[/site.css]]" />
<!-- Globbed link tag with comma separated pattern -->
<link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/sub/site2.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/sub/site3.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/sub/site3.min.css]]" />
<!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/sub/site2.css]]" />

View File

@ -10,6 +10,9 @@
<!-- Globbed link tag with existing file -->
<link rel="stylesheet" title="&lt;the title>" href="/site.css" />
<!-- Globbed link tag with comma separated pattern -->
<link rel="stylesheet" title="&lt;the title" href="/site.css" /><link rel="stylesheet" title="&lt;the title" href="/sub/site2.css" /><link rel="stylesheet" title="&lt;the title" href="/sub/site3.css" /><link rel="stylesheet" title="&lt;the title" href="/sub/site3.min.css" />
<!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" title="&quot;the&quot; title" href="/site.css" /><link rel="stylesheet" title="&quot;the&quot; title" href="/sub/site2.css" />

View 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="HtmlEncode[[/site.js]]"></script><script src="HtmlEncode[[/sub/site2.js]]"></script><script src="HtmlEncode[[/sub/site3.js]]"></script>
<!-- Globbed script tag with missing file -->

View 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 -->

View 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

View File

@ -12,8 +12,11 @@
<!-- Globbed link tag with existing file -->
<link asp-href-include="**/site.css" rel="stylesheet" title="&lt;the title>" />
<!-- Globbed link tag with comma separated pattern -->
<link asp-href-include="~/*.css, ~/sub/*.css" rel="stylesheet" title="&lt;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"

View File

@ -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>