Prevent @import in scoped CSS (#25196)
* Reject @import rules in scoped CSS files * CR feedback: Use SourceText * CR feedback: Another test case * Use same file reading mechanism as "generate" command
This commit is contained in:
parent
64c47b733f
commit
a9b596e091
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -909,5 +909,21 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region "CSS Rewriter Errors"
|
||||||
|
|
||||||
|
// CSS Rewriter Errors ID Offset = 5000
|
||||||
|
|
||||||
|
internal static readonly RazorDiagnosticDescriptor CssRewriting_ImportNotAllowed =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}5000",
|
||||||
|
() => Resources.CssRewriter_ImportNotAllowed,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateCssRewriting_ImportNotAllowed(SourceSpan location)
|
||||||
|
{
|
||||||
|
return RazorDiagnostic.Create(CssRewriting_ImportNotAllowed, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -562,4 +562,7 @@
|
||||||
<data name="DirectiveExpectsBooleanLiteral" xml:space="preserve">
|
<data name="DirectiveExpectsBooleanLiteral" xml:space="preserve">
|
||||||
<value>The '{0}' directive expects a boolean literal.</value>
|
<value>The '{0}' directive expects a boolean literal.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CssRewriter_ImportNotAllowed" xml:space="preserve">
|
||||||
|
<value>@import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using Microsoft.Css.Parser.Parser;
|
using Microsoft.Css.Parser.Parser;
|
||||||
using Microsoft.Css.Parser.Tokens;
|
using Microsoft.Css.Parser.Tokens;
|
||||||
using Microsoft.Css.Parser.TreeItems;
|
using Microsoft.Css.Parser.TreeItems;
|
||||||
|
|
@ -57,28 +60,55 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
protected override Task<int> ExecuteCoreAsync()
|
protected override Task<int> ExecuteCoreAsync()
|
||||||
{
|
{
|
||||||
|
var allDiagnostics = new ConcurrentQueue<RazorDiagnostic>();
|
||||||
|
|
||||||
Parallel.For(0, Sources.Values.Count, i =>
|
Parallel.For(0, Sources.Values.Count, i =>
|
||||||
{
|
{
|
||||||
var source = Sources.Values[i];
|
var source = Sources.Values[i];
|
||||||
var output = Outputs.Values[i];
|
var output = Outputs.Values[i];
|
||||||
var cssScope = CssScopes.Values[i];
|
var cssScope = CssScopes.Values[i];
|
||||||
|
|
||||||
var inputText = File.ReadAllText(source);
|
using var inputSourceStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
|
||||||
var rewrittenCss = AddScopeToSelectors(inputText, cssScope);
|
var inputSourceText = SourceText.From(inputSourceStream);
|
||||||
File.WriteAllText(output, rewrittenCss);
|
|
||||||
|
var rewrittenCss = AddScopeToSelectors(source, inputSourceText, cssScope, out var diagnostics);
|
||||||
|
if (diagnostics.Any())
|
||||||
|
{
|
||||||
|
foreach (var diagnostic in diagnostics)
|
||||||
|
{
|
||||||
|
allDiagnostics.Enqueue(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
File.WriteAllText(output, rewrittenCss);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Task.FromResult(ExitCodeSuccess);
|
foreach (var diagnostic in allDiagnostics)
|
||||||
|
{
|
||||||
|
Error.WriteLine(diagnostic.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(allDiagnostics.Any() ? ExitCodeFailure : ExitCodeSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public for tests
|
// Public for tests
|
||||||
public static string AddScopeToSelectors(string inputText, string cssScope)
|
public static string AddScopeToSelectors(string filePath, string inputSource, string cssScope, out IEnumerable<RazorDiagnostic> diagnostics)
|
||||||
|
=> AddScopeToSelectors(filePath, SourceText.From(inputSource), cssScope, out diagnostics);
|
||||||
|
|
||||||
|
private static string AddScopeToSelectors(string filePath, SourceText inputSourceText, string cssScope, out IEnumerable<RazorDiagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var cssParser = new DefaultParserFactory().CreateParser();
|
var cssParser = new DefaultParserFactory().CreateParser();
|
||||||
|
var inputText = inputSourceText.ToString();
|
||||||
var stylesheet = cssParser.Parse(inputText, insertComments: false);
|
var stylesheet = cssParser.Parse(inputText, insertComments: false);
|
||||||
|
|
||||||
var resultBuilder = new StringBuilder();
|
var resultBuilder = new StringBuilder();
|
||||||
var previousInsertionPosition = 0;
|
var previousInsertionPosition = 0;
|
||||||
|
var foundDiagnostics = new List<RazorDiagnostic>();
|
||||||
|
|
||||||
|
var ensureNoImportsVisitor = new EnsureNoImports(filePath, inputSourceText, stylesheet, foundDiagnostics);
|
||||||
|
ensureNoImportsVisitor.Visit();
|
||||||
|
|
||||||
var scopeInsertionPositionsVisitor = new FindScopeInsertionEdits(stylesheet);
|
var scopeInsertionPositionsVisitor = new FindScopeInsertionEdits(stylesheet);
|
||||||
scopeInsertionPositionsVisitor.Visit();
|
scopeInsertionPositionsVisitor.Visit();
|
||||||
|
|
@ -105,6 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
||||||
resultBuilder.Append(inputText.Substring(previousInsertionPosition));
|
resultBuilder.Append(inputText.Substring(previousInsertionPosition));
|
||||||
|
|
||||||
|
diagnostics = foundDiagnostics;
|
||||||
return resultBuilder.ToString();
|
return resultBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,6 +288,36 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EnsureNoImports : Visitor
|
||||||
|
{
|
||||||
|
private readonly string _filePath;
|
||||||
|
private readonly SourceText _sourceText;
|
||||||
|
private readonly List<RazorDiagnostic> _diagnostics;
|
||||||
|
|
||||||
|
public EnsureNoImports(string filePath, SourceText sourceText, ComplexItem root, List<RazorDiagnostic> diagnostics) : base(root)
|
||||||
|
{
|
||||||
|
_filePath = filePath;
|
||||||
|
_sourceText = sourceText;
|
||||||
|
_diagnostics = diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void VisitAtDirective(AtDirective item)
|
||||||
|
{
|
||||||
|
if (item.Children.Count >= 2
|
||||||
|
&& item.Children[0] is TokenItem firstChild
|
||||||
|
&& firstChild.TokenType == CssTokenType.At
|
||||||
|
&& item.Children[1] is TokenItem secondChild
|
||||||
|
&& string.Equals(secondChild.Text, "import", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var linePosition = _sourceText.Lines.GetLinePosition(item.Start);
|
||||||
|
var sourceSpan = new SourceSpan(_filePath, item.Start, linePosition.Line, linePosition.Character, item.Length);
|
||||||
|
_diagnostics.Add(RazorDiagnosticFactory.CreateCssRewriting_ImportNotAllowed(sourceSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
base.VisitAtDirective(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class Visitor
|
private class Visitor
|
||||||
{
|
{
|
||||||
private readonly ComplexItem _root;
|
private readonly ComplexItem _root;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Tools
|
namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
|
|
@ -11,9 +12,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void HandlesEmptyFile()
|
public void HandlesEmptyFile()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(string.Empty, "TestScope");
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", string.Empty, "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(string.Empty, result);
|
Assert.Equal(string.Empty, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,11 +23,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void AddsScopeAfterSelector()
|
public void AddsScopeAfterSelector()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.myclass { color: red; }
|
.myclass { color: red; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.myclass[TestScope] { color: red; }
|
.myclass[TestScope] { color: red; }
|
||||||
", result);
|
", result);
|
||||||
|
|
@ -35,12 +38,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void HandlesMultipleSelectors()
|
public void HandlesMultipleSelectors()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.first, .second { color: red; }
|
.first, .second { color: red; }
|
||||||
.third { color: blue; }
|
.third { color: blue; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.first[TestScope], .second[TestScope] { color: red; }
|
.first[TestScope], .second[TestScope] { color: red; }
|
||||||
.third[TestScope] { color: blue; }
|
.third[TestScope] { color: blue; }
|
||||||
|
|
@ -51,11 +55,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void HandlesComplexSelectors()
|
public void HandlesComplexSelectors()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.first div > li, body .second:not(.fancy)[attr~=whatever] { color: red; }
|
.first div > li, body .second:not(.fancy)[attr~=whatever] { color: red; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.first div > li[TestScope], body .second:not(.fancy)[attr~=whatever][TestScope] { color: red; }
|
.first div > li[TestScope], body .second:not(.fancy)[attr~=whatever][TestScope] { color: red; }
|
||||||
", result);
|
", result);
|
||||||
|
|
@ -65,11 +70,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void HandlesSpacesAndCommentsWithinSelectors()
|
public void HandlesSpacesAndCommentsWithinSelectors()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.first /* space at end {} */ div , .myclass /* comment at end */ { color: red; }
|
.first /* space at end {} */ div , .myclass /* comment at end */ { color: red; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.first /* space at end {} */ div[TestScope] , .myclass[TestScope] /* comment at end */ { color: red; }
|
.first /* space at end {} */ div[TestScope] , .myclass[TestScope] /* comment at end */ { color: red; }
|
||||||
", result);
|
", result);
|
||||||
|
|
@ -79,12 +85,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RespectsDeepCombinator()
|
public void RespectsDeepCombinator()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.first ::deep .second { color: red; }
|
.first ::deep .second { color: red; }
|
||||||
a ::deep b, c ::deep d { color: blue; }
|
a ::deep b, c ::deep d { color: blue; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.first[TestScope] .second { color: red; }
|
.first[TestScope] .second { color: red; }
|
||||||
a[TestScope] b, c[TestScope] d { color: blue; }
|
a[TestScope] b, c[TestScope] d { color: blue; }
|
||||||
|
|
@ -95,12 +102,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RespectsDeepCombinatorWithDirectDescendant()
|
public void RespectsDeepCombinatorWithDirectDescendant()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
a > ::deep b { color: red; }
|
a > ::deep b { color: red; }
|
||||||
c ::deep > d { color: blue; }
|
c ::deep > d { color: blue; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
a[TestScope] > b { color: red; }
|
a[TestScope] > b { color: red; }
|
||||||
c[TestScope] > d { color: blue; }
|
c[TestScope] > d { color: blue; }
|
||||||
|
|
@ -111,12 +119,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RespectsDeepCombinatorWithAdjacentSibling()
|
public void RespectsDeepCombinatorWithAdjacentSibling()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
a + ::deep b { color: red; }
|
a + ::deep b { color: red; }
|
||||||
c ::deep + d { color: blue; }
|
c ::deep + d { color: blue; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
a[TestScope] + b { color: red; }
|
a[TestScope] + b { color: red; }
|
||||||
c[TestScope] + d { color: blue; }
|
c[TestScope] + d { color: blue; }
|
||||||
|
|
@ -127,12 +136,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RespectsDeepCombinatorWithGeneralSibling()
|
public void RespectsDeepCombinatorWithGeneralSibling()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
a ~ ::deep b { color: red; }
|
a ~ ::deep b { color: red; }
|
||||||
c ::deep ~ d { color: blue; }
|
c ::deep ~ d { color: blue; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
a[TestScope] ~ b { color: red; }
|
a[TestScope] ~ b { color: red; }
|
||||||
c[TestScope] ~ d { color: blue; }
|
c[TestScope] ~ d { color: blue; }
|
||||||
|
|
@ -143,11 +153,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void IgnoresMultipleDeepCombinators()
|
public void IgnoresMultipleDeepCombinators()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.first ::deep .second ::deep .third { color:red; }
|
.first ::deep .second ::deep .third { color:red; }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.first[TestScope] .second ::deep .third { color:red; }
|
.first[TestScope] .second ::deep .third { color:red; }
|
||||||
", result);
|
", result);
|
||||||
|
|
@ -157,13 +168,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RespectsDeepCombinatorWithSpacesAndComments()
|
public void RespectsDeepCombinatorWithSpacesAndComments()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.a .b /* comment ::deep 1 */ ::deep /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
.a .b /* comment ::deep 1 */ ::deep /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
||||||
::deep * { color: blue; } /* Leading deep combinator */
|
::deep * { color: blue; } /* Leading deep combinator */
|
||||||
another ::deep { color: green } /* Trailing deep combinator */
|
another ::deep { color: green } /* Trailing deep combinator */
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.a .b[TestScope] /* comment ::deep 1 */ /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
.a .b[TestScope] /* comment ::deep 1 */ /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; }
|
||||||
[TestScope] * { color: blue; } /* Leading deep combinator */
|
[TestScope] * { color: blue; } /* Leading deep combinator */
|
||||||
|
|
@ -175,7 +187,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void HandlesAtBlocks()
|
public void HandlesAtBlocks()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.myclass { color: red; }
|
.myclass { color: red; }
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
|
|
@ -183,9 +195,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
content: 'This should not be a selector: .fake-selector { color: red }'
|
content: 'This should not be a selector: .fake-selector { color: red }'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.myclass[TestScope] { color: red; }
|
.myclass[TestScope] { color: red; }
|
||||||
|
|
||||||
|
|
@ -201,11 +214,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void AddsScopeToKeyframeNames()
|
public void AddsScopeToKeyframeNames()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
@keyframes my-animation { /* whatever */ }
|
@keyframes my-animation { /* whatever */ }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
@keyframes my-animation-TestScope { /* whatever */ }
|
@keyframes my-animation-TestScope { /* whatever */ }
|
||||||
", result);
|
", result);
|
||||||
|
|
@ -215,7 +229,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RewritesAnimationNamesWhenMatchingKnownKeyframes()
|
public void RewritesAnimationNamesWhenMatchingKnownKeyframes()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.myclass {
|
.myclass {
|
||||||
color: red;
|
color: red;
|
||||||
animation: /* ignore comment */ my-animation 1s infinite;
|
animation: /* ignore comment */ my-animation 1s infinite;
|
||||||
|
|
@ -228,9 +242,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
@keyframes my-animation { /* whatever */ }
|
@keyframes my-animation { /* whatever */ }
|
||||||
@keyframes different-animation { /* whatever */ }
|
@keyframes different-animation { /* whatever */ }
|
||||||
@keyframes unused-animation { /* whatever */ }
|
@keyframes unused-animation { /* whatever */ }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.myclass[TestScope] {
|
.myclass[TestScope] {
|
||||||
color: red;
|
color: red;
|
||||||
|
|
@ -251,14 +266,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
public void RewritesMultipleAnimationNames()
|
public void RewritesMultipleAnimationNames()
|
||||||
{
|
{
|
||||||
// Arrange/act
|
// Arrange/act
|
||||||
var result = RewriteCssCommand.AddScopeToSelectors(@"
|
var result = RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
.myclass1 { animation-name: my-animation , different-animation }
|
.myclass1 { animation-name: my-animation , different-animation }
|
||||||
.myclass2 { animation: 4s linear 0s alternate my-animation infinite, different-animation 0s }
|
.myclass2 { animation: 4s linear 0s alternate my-animation infinite, different-animation 0s }
|
||||||
@keyframes my-animation { }
|
@keyframes my-animation { }
|
||||||
@keyframes different-animation { }
|
@keyframes different-animation { }
|
||||||
", "TestScope");
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
Assert.Empty(diagnostics);
|
||||||
Assert.Equal(@"
|
Assert.Equal(@"
|
||||||
.myclass1[TestScope] { animation-name: my-animation-TestScope , different-animation-TestScope }
|
.myclass1[TestScope] { animation-name: my-animation-TestScope , different-animation-TestScope }
|
||||||
.myclass2[TestScope] { animation: 4s linear 0s alternate my-animation-TestScope infinite, different-animation-TestScope 0s }
|
.myclass2[TestScope] { animation: 4s linear 0s alternate my-animation-TestScope infinite, different-animation-TestScope 0s }
|
||||||
|
|
@ -266,5 +282,27 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
||||||
@keyframes different-animation-TestScope { }
|
@keyframes different-animation-TestScope { }
|
||||||
", result);
|
", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RejectsImportStatements()
|
||||||
|
{
|
||||||
|
// Arrange/act
|
||||||
|
RewriteCssCommand.AddScopeToSelectors("file.css", @"
|
||||||
|
@import ""basic-import.css"";
|
||||||
|
@import ""import-with-media-type.css"" print;
|
||||||
|
@import ""import-with-media-query.css"" screen and (orientation:landscape);
|
||||||
|
@ImPoRt /* comment */ ""scheme://path/to/complex-import"" /* another-comment */ screen;
|
||||||
|
@otheratrule ""should-not-cause-error.css"";
|
||||||
|
/* @import ""should-be-ignored-because-it-is-in-a-comment.css""; */
|
||||||
|
.myclass { color: red; }
|
||||||
|
", "TestScope", out var diagnostics);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(diagnostics,
|
||||||
|
diagnostic => Assert.Equal("file.css(2,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()),
|
||||||
|
diagnostic => Assert.Equal("file.css(3,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()),
|
||||||
|
diagnostic => Assert.Equal("file.css(4,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()),
|
||||||
|
diagnostic => Assert.Equal("file.css(5,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue