Merge branch 'master' of https://github.com/connorskees/grass into modules
This commit is contained in:
commit
d043167015
@ -33,7 +33,7 @@ pub(crate) fn simple_selectors(mut args: CallArgs, parser: &mut Parser<'_>) -> S
|
||||
}
|
||||
|
||||
let compound = if let Some(ComplexSelectorComponent::Compound(compound)) =
|
||||
selector.0.components[0].components.get(0).cloned()
|
||||
selector.0.components[0].components.first().cloned()
|
||||
{
|
||||
compound
|
||||
} else {
|
||||
|
@ -129,8 +129,8 @@ impl Css {
|
||||
for rule in body {
|
||||
match rule {
|
||||
Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule)?),
|
||||
Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(s),
|
||||
Stmt::Comment(s) => vals.get_mut(0).unwrap().push_comment(s),
|
||||
Stmt::Style(s) => vals.first_mut().unwrap().push_style(s),
|
||||
Stmt::Comment(s) => vals.first_mut().unwrap().push_comment(s),
|
||||
Stmt::Media(m) => {
|
||||
let MediaRule { query, body, .. } = *m;
|
||||
vals.push(Toplevel::Media { query, body })
|
||||
@ -167,7 +167,7 @@ impl Css {
|
||||
k @ Stmt::KeyframesRuleSet(..) => {
|
||||
unreachable!("@keyframes ruleset {:?}", k)
|
||||
}
|
||||
Stmt::Import(s) => vals.get_mut(0).unwrap().push_import(s),
|
||||
Stmt::Import(s) => vals.first_mut().unwrap().push_import(s),
|
||||
};
|
||||
}
|
||||
vals
|
||||
@ -204,7 +204,7 @@ impl Css {
|
||||
let mut vals = vec![Toplevel::new_keyframes_rule(selector)];
|
||||
for rule in body {
|
||||
match rule {
|
||||
Stmt::Style(s) => vals.get_mut(0).unwrap().push_style(s),
|
||||
Stmt::Style(s) => vals.first_mut().unwrap().push_style(s),
|
||||
Stmt::KeyframesRuleSet(..) => vals.extend(self.parse_stmt(rule)?),
|
||||
_ => todo!(),
|
||||
}
|
||||
@ -221,7 +221,7 @@ impl Css {
|
||||
// this is how we print newlines between unrelated styles
|
||||
// it could probably be refactored
|
||||
if !v.is_empty() {
|
||||
if let Some(Toplevel::MultilineComment(..)) = v.get(0) {
|
||||
if let Some(Toplevel::MultilineComment(..)) = v.first() {
|
||||
} else if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
|
@ -341,13 +341,15 @@ impl<'a> Parser<'a> {
|
||||
return Err(("expected \")\".", pos).into());
|
||||
}
|
||||
}
|
||||
Some(..) => unreachable!(),
|
||||
Some(Token { pos, .. }) => {
|
||||
return Err(("expected \")\".", *pos).into());
|
||||
}
|
||||
None => return Err(("expected \")\".", span).into()),
|
||||
}
|
||||
}
|
||||
Some(c) => {
|
||||
Some(Token { pos, .. }) => {
|
||||
value?;
|
||||
unreachable!("{:?}", c)
|
||||
return Err(("expected \")\".", *pos).into());
|
||||
}
|
||||
None => return Err(("expected \")\".", span).into()),
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ impl<'a> Parser<'a> {
|
||||
return Err(("Functions may not be declared in control directives.", span).into());
|
||||
}
|
||||
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
if RESERVED_IDENTIFIERS.contains(&unvendor(&name)) {
|
||||
return Err(("Invalid function name.", span).into());
|
||||
}
|
||||
|
@ -112,6 +112,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
pub(super) fn import(&mut self) -> SassResult<Vec<Stmt>> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
self.whitespace();
|
||||
|
||||
match self.toks.peek() {
|
||||
|
@ -186,6 +186,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
pub(super) fn parse_keyframes(&mut self, rule: String) -> SassResult<Stmt> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
let name = self.parse_keyframes_name()?;
|
||||
|
||||
self.whitespace();
|
||||
|
@ -66,6 +66,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
pub(super) fn parse_include(&mut self) -> SassResult<Vec<Stmt>> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
self.whitespace_or_comment();
|
||||
let name = self.parse_identifier()?.map_node(Into::into);
|
||||
|
||||
|
@ -310,6 +310,14 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
AtRuleKind::AtRoot => {
|
||||
if self.flags.in_function() {
|
||||
return Err((
|
||||
"This at-rule is not allowed here.",
|
||||
kind_string.span,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if self.at_root {
|
||||
stmts.append(&mut self.parse_at_root()?);
|
||||
} else {
|
||||
@ -365,6 +373,14 @@ impl<'a> Parser<'a> {
|
||||
AtRuleKind::For => stmts.append(&mut self.parse_for()?),
|
||||
AtRuleKind::While => stmts.append(&mut self.parse_while()?),
|
||||
AtRuleKind::Charset => {
|
||||
if self.flags.in_function() {
|
||||
return Err((
|
||||
"This at-rule is not allowed here.",
|
||||
kind_string.span,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
read_until_semicolon_or_closing_curly_brace(self.toks)?;
|
||||
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
|
||||
self.toks.next();
|
||||
@ -401,7 +417,11 @@ impl<'a> Parser<'a> {
|
||||
self.whitespace();
|
||||
match comment.node {
|
||||
Comment::Silent => continue,
|
||||
Comment::Loud(s) => stmts.push(Stmt::Comment(s)),
|
||||
Comment::Loud(s) => {
|
||||
if !self.flags.in_function() {
|
||||
stmts.push(Stmt::Comment(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'\u{0}'..='\u{8}' | '\u{b}'..='\u{1f}' => {
|
||||
@ -414,6 +434,13 @@ impl<'a> Parser<'a> {
|
||||
// dart-sass seems to special-case the error message here?
|
||||
'!' | '{' => return Err(("expected \"}\".", *pos).into()),
|
||||
_ => {
|
||||
if self.flags.in_function() {
|
||||
return Err((
|
||||
"Functions can only contain variable declarations and control directives.",
|
||||
self.span_before
|
||||
)
|
||||
.into());
|
||||
}
|
||||
if self.flags.in_keyframes() {
|
||||
match self.is_selector_or_style()? {
|
||||
SelectorOrStyle::Style(property, value) => {
|
||||
@ -526,6 +553,12 @@ impl<'a> Parser<'a> {
|
||||
found_curly = true;
|
||||
break;
|
||||
}
|
||||
'\\' => {
|
||||
string.push('\\');
|
||||
if let Some(Token { kind, .. }) = self.toks.next() {
|
||||
string.push(kind);
|
||||
}
|
||||
}
|
||||
'!' => {
|
||||
if peek_ident_no_interpolation(self.toks, false, self.span_before)?.node
|
||||
== "optional"
|
||||
@ -718,6 +751,10 @@ impl<'a> Parser<'a> {
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn parse_unknown_at_rule(&mut self, name: String) -> SassResult<Stmt> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
let mut params = String::new();
|
||||
self.whitespace();
|
||||
if let Some(Token { kind: ';', .. }) | None = self.toks.peek() {
|
||||
@ -781,6 +818,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn parse_media(&mut self) -> SassResult<Stmt> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
let query = self.parse_media_query_list()?;
|
||||
|
||||
self.whitespace();
|
||||
@ -877,6 +918,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn parse_extend(&mut self) -> SassResult<()> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
// todo: track when inside ruleset or `@content`
|
||||
// if !self.in_style_rule && !self.in_mixin && !self.in_content_block {
|
||||
// return Err(("@extend may only be used within style rules.", self.span_before).into());
|
||||
@ -945,6 +989,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn parse_supports(&mut self) -> SassResult<Stmt> {
|
||||
if self.flags.in_function() {
|
||||
return Err(("This at-rule is not allowed here.", self.span_before).into());
|
||||
}
|
||||
|
||||
let params = self.parse_media_args()?;
|
||||
|
||||
if params.is_empty() {
|
||||
|
@ -15,8 +15,7 @@ use crate::{
|
||||
error::SassResult,
|
||||
unit::Unit,
|
||||
utils::{
|
||||
devour_whitespace, eat_whole_number, read_until_closing_paren,
|
||||
read_until_closing_square_brace, IsWhitespace, ParsedNumber,
|
||||
devour_whitespace, eat_whole_number, read_until_closing_paren, IsWhitespace, ParsedNumber,
|
||||
},
|
||||
value::{Number, SassFunction, SassMap, Value},
|
||||
Token,
|
||||
@ -30,7 +29,6 @@ use super::super::Parser;
|
||||
enum IntermediateValue {
|
||||
Value(HigherIntermediateValue),
|
||||
Op(Op),
|
||||
Bracketed(Vec<Token>),
|
||||
Paren(Vec<Token>),
|
||||
Comma,
|
||||
Whitespace,
|
||||
@ -107,7 +105,7 @@ impl<'a> Parser<'a> {
|
||||
comma_separated.push(space_separated.pop().unwrap());
|
||||
} else {
|
||||
let mut span = space_separated
|
||||
.get(0)
|
||||
.first()
|
||||
.ok_or(("Expected expression.", val.span))?
|
||||
.span;
|
||||
comma_separated.push(
|
||||
@ -127,22 +125,6 @@ impl<'a> Parser<'a> {
|
||||
);
|
||||
}
|
||||
}
|
||||
IntermediateValue::Bracketed(t) => {
|
||||
last_was_whitespace = false;
|
||||
space_separated.push(
|
||||
HigherIntermediateValue::Literal(
|
||||
match iter.parser.parse_value_from_vec(t, in_paren)?.node {
|
||||
Value::List(v, sep, Brackets::None) => {
|
||||
Value::List(v, sep, Brackets::Bracketed)
|
||||
}
|
||||
v => {
|
||||
Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
|
||||
}
|
||||
},
|
||||
)
|
||||
.span(val.span),
|
||||
)
|
||||
}
|
||||
IntermediateValue::Paren(t) => {
|
||||
last_was_whitespace = false;
|
||||
space_separated.push(iter.parse_paren(Spanned {
|
||||
@ -657,28 +639,42 @@ impl<'a> Parser<'a> {
|
||||
.span(span_start.merge(span))
|
||||
}
|
||||
'[' => {
|
||||
let mut span = self.toks.next().unwrap().pos();
|
||||
let mut span = self.span_before;
|
||||
self.toks.next();
|
||||
self.whitespace_or_comment();
|
||||
let mut inner = match read_until_closing_square_brace(self.toks) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
if let Some(last_tok) = inner.pop() {
|
||||
if last_tok.kind != ']' {
|
||||
return Some(Err(("expected \"]\".", span).into()));
|
||||
}
|
||||
span = span.merge(last_tok.pos());
|
||||
}
|
||||
if inner.is_empty() {
|
||||
|
||||
if let Some(Token { kind: ']', pos }) = self.toks.peek() {
|
||||
span = span.merge(*pos);
|
||||
self.toks.next();
|
||||
IntermediateValue::Value(HigherIntermediateValue::Literal(Value::List(
|
||||
Vec::new(),
|
||||
ListSeparator::Space,
|
||||
Brackets::Bracketed,
|
||||
)))
|
||||
.span(span)
|
||||
} else {
|
||||
IntermediateValue::Bracketed(inner)
|
||||
// todo: we don't know if we're `in_paren` here
|
||||
let inner = match self.parse_value(false, &|toks| {
|
||||
matches!(toks.peek(), Some(Token { kind: ']', .. }))
|
||||
}) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
|
||||
span = span.merge(inner.span);
|
||||
|
||||
if !matches!(self.toks.next(), Some(Token { kind: ']', .. })) {
|
||||
return Some(Err(("expected \"]\".", span).into()));
|
||||
}
|
||||
|
||||
IntermediateValue::Value(HigherIntermediateValue::Literal(match inner.node {
|
||||
Value::List(els, sep, Brackets::None) => {
|
||||
Value::List(els, sep, Brackets::Bracketed)
|
||||
}
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed),
|
||||
}))
|
||||
.span(span)
|
||||
}
|
||||
.span(span)
|
||||
}
|
||||
'$' => {
|
||||
self.toks.next();
|
||||
@ -1180,14 +1176,6 @@ impl<'a, 'b: 'a> IntermediateValueIterator<'a, 'b> {
|
||||
IntermediateValue::Comma => {
|
||||
return Err(("Expected expression.", self.parser.span_before).into())
|
||||
}
|
||||
IntermediateValue::Bracketed(t) => {
|
||||
let v = self.parser.parse_value_from_vec(t, in_paren)?;
|
||||
HigherIntermediateValue::Literal(match v.node {
|
||||
Value::List(v, sep, Brackets::None) => Value::List(v, sep, Brackets::Bracketed),
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed),
|
||||
})
|
||||
.span(v.span)
|
||||
}
|
||||
IntermediateValue::Paren(t) => {
|
||||
let val = self.parse_paren(Spanned {
|
||||
node: t,
|
||||
|
@ -186,7 +186,13 @@ fn weave_parents(
|
||||
for group in lcs {
|
||||
choices.push(
|
||||
chunks(&mut groups_one, &mut groups_two, |sequence| {
|
||||
complex_is_parent_superselector(sequence.get(0).unwrap().clone(), group.clone())
|
||||
complex_is_parent_superselector(
|
||||
match sequence.get(0) {
|
||||
Some(v) => v.clone(),
|
||||
None => return true,
|
||||
},
|
||||
group.clone(),
|
||||
)
|
||||
})
|
||||
.into_iter()
|
||||
.map(|chunk| chunk.into_iter().flatten().collect())
|
||||
@ -340,16 +346,8 @@ fn merge_final_combinators(
|
||||
) -> Option<Vec<Vec<Vec<ComplexSelectorComponent>>>> {
|
||||
let mut result = result.unwrap_or_default();
|
||||
|
||||
if (components_one.is_empty()
|
||||
|| !components_one
|
||||
.get(components_one.len() - 1)
|
||||
.unwrap()
|
||||
.is_combinator())
|
||||
&& (components_two.is_empty()
|
||||
|| !components_two
|
||||
.get(components_two.len() - 1)
|
||||
.unwrap()
|
||||
.is_combinator())
|
||||
if (components_one.is_empty() || !components_one.back().unwrap().is_combinator())
|
||||
&& (components_two.is_empty() || !components_two.back().unwrap().is_combinator())
|
||||
{
|
||||
return Some(Vec::from(result));
|
||||
}
|
||||
@ -533,12 +531,8 @@ fn merge_final_combinators(
|
||||
}
|
||||
(Some(combinator_one), None) => {
|
||||
if *combinator_one == Combinator::Child && !components_two.is_empty() {
|
||||
if let Some(ComplexSelectorComponent::Compound(c1)) =
|
||||
components_one.get(components_one.len() - 1)
|
||||
{
|
||||
if let Some(ComplexSelectorComponent::Compound(c2)) =
|
||||
components_two.get(components_two.len() - 1)
|
||||
{
|
||||
if let Some(ComplexSelectorComponent::Compound(c1)) = components_one.back() {
|
||||
if let Some(ComplexSelectorComponent::Compound(c2)) = components_two.back() {
|
||||
if c2.is_super_selector(c1, &None) {
|
||||
components_two.pop_back();
|
||||
}
|
||||
@ -555,12 +549,8 @@ fn merge_final_combinators(
|
||||
}
|
||||
(None, Some(combinator_two)) => {
|
||||
if *combinator_two == Combinator::Child && !components_one.is_empty() {
|
||||
if let Some(ComplexSelectorComponent::Compound(c1)) =
|
||||
components_one.get(components_one.len() - 1)
|
||||
{
|
||||
if let Some(ComplexSelectorComponent::Compound(c2)) =
|
||||
components_two.get(components_two.len() - 1)
|
||||
{
|
||||
if let Some(ComplexSelectorComponent::Compound(c1)) = components_one.back() {
|
||||
if let Some(ComplexSelectorComponent::Compound(c2)) = components_two.back() {
|
||||
if c1.is_super_selector(c2, &None) {
|
||||
components_one.pop_back();
|
||||
}
|
||||
|
@ -561,7 +561,7 @@ fn is_simple_selector_start(c: char) -> bool {
|
||||
/// with pseudo-class syntax (`:before`, `:after`, `:first-line`, or
|
||||
/// `:first-letter`)
|
||||
fn is_fake_pseudo_element(name: &str) -> bool {
|
||||
match name.as_bytes().get(0) {
|
||||
match name.as_bytes().first() {
|
||||
Some(b'a') | Some(b'A') => name.to_ascii_lowercase() == "after",
|
||||
Some(b'b') | Some(b'B') => name.to_ascii_lowercase() == "before",
|
||||
Some(b'f') | Some(b'F') => match name.to_ascii_lowercase().as_str() {
|
||||
|
@ -364,7 +364,7 @@ impl SimpleSelector {
|
||||
};
|
||||
complex
|
||||
.components
|
||||
.get(0)
|
||||
.first()
|
||||
.unwrap()
|
||||
.as_compound()
|
||||
.components
|
||||
|
@ -133,7 +133,8 @@ pub(crate) fn peek_escape(toks: &mut PeekMoreIterator<IntoIter<Token>>) -> SassR
|
||||
toks.peek_forward(1);
|
||||
}
|
||||
} else {
|
||||
value = toks.peek_forward(1).unwrap().kind as u32;
|
||||
value = first.kind as u32;
|
||||
toks.advance_cursor();
|
||||
}
|
||||
|
||||
let c = std::char::from_u32(value).ok_or(("Invalid escape sequence.", span))?;
|
||||
|
@ -235,39 +235,3 @@ pub(crate) fn read_until_closing_paren(
|
||||
}
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
pub(crate) fn read_until_closing_square_brace(
|
||||
toks: &mut PeekMoreIterator<IntoIter<Token>>,
|
||||
) -> SassResult<Vec<Token>> {
|
||||
let mut t = Vec::new();
|
||||
let mut scope = 0;
|
||||
while let Some(tok) = toks.next() {
|
||||
// TODO: comments
|
||||
match tok.kind {
|
||||
']' => {
|
||||
if scope < 1 {
|
||||
t.push(tok);
|
||||
return Ok(t);
|
||||
} else {
|
||||
scope -= 1;
|
||||
}
|
||||
}
|
||||
'[' => scope += 1,
|
||||
'"' | '\'' => {
|
||||
t.push(tok);
|
||||
t.extend(read_until_closing_quote(toks, tok.kind)?);
|
||||
continue;
|
||||
}
|
||||
'\\' => {
|
||||
t.push(toks.next().unwrap());
|
||||
t.push(match toks.next() {
|
||||
Some(tok) => tok,
|
||||
None => continue,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
t.push(tok)
|
||||
}
|
||||
Ok(t)
|
||||
}
|
||||
|
@ -168,6 +168,14 @@ error!(
|
||||
filter_value_after_equal_is_last_char,
|
||||
"a {\n color: foo(a=a", "Error: expected \")\"."
|
||||
);
|
||||
error!(
|
||||
unclosed_paren_in_nested_args,
|
||||
"a { color: a(b(red); }", "Error: expected \")\"."
|
||||
);
|
||||
error!(
|
||||
filter_rhs_missing_closing_paren,
|
||||
"a { color: lighten(red=(green); }", "Error: expected \")\"."
|
||||
);
|
||||
test!(
|
||||
space_after_loud_comment,
|
||||
"@mixin foo($x) {
|
||||
|
@ -245,3 +245,11 @@ error!(
|
||||
"a {foo: {bar: red", "Error: Expected identifier."
|
||||
);
|
||||
error!(toplevel_nullbyte, "\u{0}", "Error: expected selector.");
|
||||
error!(
|
||||
double_escaped_bang_at_toplevel,
|
||||
"\\!\\!", "Error: expected \"{\"."
|
||||
);
|
||||
error!(
|
||||
nothing_after_escape_inside_brackets,
|
||||
"a { color: [\\", "Error: Expected expression."
|
||||
);
|
||||
|
@ -1874,6 +1874,14 @@ test!(
|
||||
}",
|
||||
"foo, a:current(foo),\n:current(foo) {\n color: black;\n}\n"
|
||||
);
|
||||
test!(
|
||||
extend_pseudo_selector_class_containing_combinator_without_rhs_selector,
|
||||
":has(a >) b {
|
||||
@extend b;
|
||||
color: red;
|
||||
}",
|
||||
":has(a >) b, :has(a >) :has(a >) :has(a >) b, :has(a >) :has(a >) :has(a >) b {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
// todo: extend_loop (massive test)
|
||||
// todo: extend tests in folders
|
||||
|
@ -195,3 +195,138 @@ test!(
|
||||
}",
|
||||
"a {\n color: foo;\n color: bar;\n}\n"
|
||||
);
|
||||
error!(
|
||||
disallows_unknown_at_rule,
|
||||
"@function foo() {
|
||||
@foo;
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_media_query,
|
||||
"@function foo() {
|
||||
@media screen {};
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_at_root,
|
||||
"@function foo() {
|
||||
@at-root {};
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_charset,
|
||||
"@function foo() {
|
||||
@charset 'utf-8';
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_extend,
|
||||
"@function foo() {
|
||||
@extend a;
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_keyframes,
|
||||
"@function foo() {
|
||||
@keyframes foo {}
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_supports,
|
||||
"@function foo() {
|
||||
@supports foo {}
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_import,
|
||||
"@function foo() {
|
||||
@import \"foo.css\";
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_inner_function_declaration,
|
||||
"@function foo() {
|
||||
@function bar() {}
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_include,
|
||||
"@function foo() {
|
||||
@include bar;
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo();
|
||||
}",
|
||||
"Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
disallows_selectors,
|
||||
"@function foo($a) {
|
||||
functiona {
|
||||
@return $a;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo(nul);
|
||||
}",
|
||||
"Error: Functions can only contain variable declarations and control directives."
|
||||
);
|
||||
test!(
|
||||
allows_multiline_comment,
|
||||
"@function foo($a) {
|
||||
/* foo */
|
||||
@return $a;
|
||||
}
|
||||
|
||||
a {
|
||||
color: foo(nul);
|
||||
}",
|
||||
"a {\n color: nul;\n}\n"
|
||||
);
|
||||
|
@ -350,6 +350,11 @@ test!(
|
||||
"ul li#foo {\n foo: bar;\n}\n"
|
||||
);
|
||||
test!(escaped_space, "a\\ b {\n color: foo;\n}\n");
|
||||
test!(
|
||||
escaped_bang,
|
||||
"\\! {\n color: red;\n}\n",
|
||||
"\\! {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
multiple_consecutive_immediate_child,
|
||||
"> > foo {\n color: foo;\n}\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user