mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-21 21:31:11 +01:00
@supports support.
This commit is contained in:
parent
0e5f8040d2
commit
0fe62a04a6
@ -11,4 +11,5 @@ export 'css/media_rule.dart';
|
||||
export 'css/node.dart';
|
||||
export 'css/style_rule.dart';
|
||||
export 'css/stylesheet.dart';
|
||||
export 'css/supports_rule.dart';
|
||||
export 'css/value.dart';
|
||||
|
@ -17,6 +17,4 @@ class CssMediaRule extends CssParentNode {
|
||||
|
||||
/*=T*/ accept/*<T>*/(CssVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitMediaRule(this);
|
||||
|
||||
String toString() => "@media ${queries.join(", ")} {${children.join(" ")}}";
|
||||
}
|
||||
|
20
lib/src/ast/css/supports_rule.dart
Normal file
20
lib/src/ast/css/supports_rule.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../visitor/interface/css.dart';
|
||||
import 'node.dart';
|
||||
import 'value.dart';
|
||||
|
||||
class CssSupportsRule extends CssParentNode {
|
||||
final CssValue<String> condition;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
CssSupportsRule(this.condition, this.span);
|
||||
|
||||
/*=T*/ accept/*<T>*/(CssVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitSupportsRule(this);
|
||||
}
|
@ -37,4 +37,10 @@ export 'sass/statement/plain_import.dart';
|
||||
export 'sass/statement/return.dart';
|
||||
export 'sass/statement/style_rule.dart';
|
||||
export 'sass/statement/stylesheet.dart';
|
||||
export 'sass/statement/supports_rule.dart';
|
||||
export 'sass/statement/variable_declaration.dart';
|
||||
export 'sass/supports_condition.dart';
|
||||
export 'sass/supports_condition/declaration.dart';
|
||||
export 'sass/supports_condition/interpolation.dart';
|
||||
export 'sass/supports_condition/negation.dart';
|
||||
export 'sass/supports_condition/operation.dart';
|
||||
|
25
lib/src/ast/sass/statement/supports_rule.dart
Normal file
25
lib/src/ast/sass/statement/supports_rule.dart
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../statement.dart';
|
||||
import '../supports_condition.dart';
|
||||
|
||||
class SupportsRule implements Statement {
|
||||
final SupportsCondition condition;
|
||||
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsRule(this.condition, Iterable<Statement> children, this.span)
|
||||
: children = new List.from(children);
|
||||
|
||||
/*=T*/ accept/*<T>*/(StatementVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitSupportsRule(this);
|
||||
|
||||
String toString() => "@supports $condition {${children.join(' ')}}";
|
||||
}
|
7
lib/src/ast/sass/supports_condition.dart
Normal file
7
lib/src/ast/sass/supports_condition.dart
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'node.dart';
|
||||
|
||||
abstract class SupportsCondition extends SassNode {}
|
20
lib/src/ast/sass/supports_condition/declaration.dart
Normal file
20
lib/src/ast/sass/supports_condition/declaration.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../expression.dart';
|
||||
import '../supports_condition.dart';
|
||||
|
||||
class SupportsDeclaration implements SupportsCondition {
|
||||
final Expression name;
|
||||
|
||||
final Expression value;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsDeclaration(this.name, this.value, this.span);
|
||||
|
||||
String toString() => "($name: $value)";
|
||||
}
|
18
lib/src/ast/sass/supports_condition/interpolation.dart
Normal file
18
lib/src/ast/sass/supports_condition/interpolation.dart
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../expression.dart';
|
||||
import '../supports_condition.dart';
|
||||
|
||||
class SupportsInterpolation implements SupportsCondition {
|
||||
final Expression expression;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsInterpolation(this.expression, this.span);
|
||||
|
||||
String toString() => "#{$expression}";
|
||||
}
|
24
lib/src/ast/sass/supports_condition/negation.dart
Normal file
24
lib/src/ast/sass/supports_condition/negation.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../supports_condition.dart';
|
||||
import 'operation.dart';
|
||||
|
||||
class SupportsNegation implements SupportsCondition {
|
||||
final SupportsCondition condition;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsNegation(this.condition, this.span);
|
||||
|
||||
String toString() {
|
||||
if (condition is SupportsNegation || condition is SupportsOperation) {
|
||||
return "not ($condition)";
|
||||
} else {
|
||||
return "not $condition";
|
||||
}
|
||||
}
|
||||
}
|
29
lib/src/ast/sass/supports_condition/operation.dart
Normal file
29
lib/src/ast/sass/supports_condition/operation.dart
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../supports_condition.dart';
|
||||
import 'negation.dart';
|
||||
|
||||
class SupportsOperation implements SupportsCondition {
|
||||
final SupportsCondition left;
|
||||
|
||||
final SupportsCondition right;
|
||||
|
||||
final String operator;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsOperation(this.left, this.right, this.operator, this.span);
|
||||
|
||||
String toString() =>
|
||||
"${_parenthesize(left)} ${operator} ${_parenthesize(right)}";
|
||||
|
||||
String _parenthesize(SupportsCondition condition) =>
|
||||
condition is SupportsNegation ||
|
||||
(condition is SupportsOperation && condition.operator == operator)
|
||||
? "($condition)"
|
||||
: condition.toString();
|
||||
}
|
@ -312,6 +312,8 @@ class Parser {
|
||||
return _mixinDeclaration(start);
|
||||
case "return":
|
||||
return _disallowedAtRule(start);
|
||||
case "supports":
|
||||
return _supportsRule(start);
|
||||
default:
|
||||
return _unknownAtRule(start, name);
|
||||
}
|
||||
@ -468,9 +470,14 @@ class Parser {
|
||||
hasContent: _mixinHasContent);
|
||||
}
|
||||
|
||||
Return _return(LineScannerState start) {
|
||||
Return _return(LineScannerState start) =>
|
||||
new Return(_expression(), _scanner.spanFrom(start));
|
||||
|
||||
SupportsRule _supportsRule(LineScannerState start) {
|
||||
var condition = _supportsCondition();
|
||||
_ignoreComments();
|
||||
return new Return(_expression(), _scanner.spanFrom(start));
|
||||
return new SupportsRule(
|
||||
condition, _children(_ruleChild), _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
AtRule _unknownAtRule(LineScannerState start, String name) {
|
||||
@ -1513,6 +1520,87 @@ class Parser {
|
||||
return buffer.interpolation(_scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
// ## Supports Conditions
|
||||
|
||||
SupportsCondition _supportsCondition() {
|
||||
var start = _scanner.state;
|
||||
var first = _scanner.peekChar();
|
||||
if (first != $lparen && first != $hash) {
|
||||
var start = _scanner.state;
|
||||
_expectCaseInsensitive("not");
|
||||
_ignoreComments();
|
||||
return new SupportsNegation(
|
||||
_supportsConditionInParens(), _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
var condition = _supportsConditionInParens();
|
||||
_ignoreComments();
|
||||
while (_lookingAtInterpolatedIdentifier()) {
|
||||
String operator;
|
||||
if (_scanCaseInsensitive("or")) {
|
||||
operator = "or";
|
||||
} else {
|
||||
_expectCaseInsensitive("and");
|
||||
operator = "and";
|
||||
}
|
||||
|
||||
_ignoreComments();
|
||||
var right = _supportsConditionInParens();
|
||||
condition = new SupportsOperation(
|
||||
condition, right, operator, _scanner.spanFrom(start));
|
||||
_ignoreComments();
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
SupportsCondition _supportsConditionInParens() {
|
||||
var start = _scanner.state;
|
||||
if (_scanner.peekChar() == $hash) {
|
||||
return new SupportsInterpolation(
|
||||
_singleInterpolation(), _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
_scanner.expectChar($lparen);
|
||||
_ignoreComments();
|
||||
var next = _scanner.peekChar();
|
||||
if (next == $lparen || next == $hash) {
|
||||
var condition = _supportsCondition();
|
||||
_ignoreComments();
|
||||
_scanner.expectChar($rparen);
|
||||
return condition;
|
||||
}
|
||||
|
||||
if (next == $n || next == $N) {
|
||||
var negation = _trySupportsNegation();
|
||||
if (negation != null) return negation;
|
||||
}
|
||||
|
||||
var name = _expression();
|
||||
_scanner.expectChar($colon);
|
||||
_ignoreComments();
|
||||
var value = _expression();
|
||||
_scanner.expectChar($rparen);
|
||||
return new SupportsDeclaration(name, value, _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
// If this fails, it puts the cursor back at the beginning.
|
||||
SupportsNegation _trySupportsNegation() {
|
||||
var start = _scanner.state;
|
||||
if (!_scanCaseInsensitive("not") || _scanner.isDone) {
|
||||
_scanner.state = start;
|
||||
return null;
|
||||
}
|
||||
|
||||
var next = _scanner.peekChar();
|
||||
if (!isWhitespace(next) && next != $lparen) {
|
||||
_scanner.state = start;
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SupportsNegation(
|
||||
_supportsConditionInParens(), _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
// ## Tokens
|
||||
|
||||
String _commentText() => _rawText(_ignoreComments);
|
||||
|
@ -12,4 +12,5 @@ abstract class CssVisitor<T> {
|
||||
T visitMediaRule(CssMediaRule node);
|
||||
T visitStyleRule(CssStyleRule node);
|
||||
T visitStylesheet(CssStylesheet node);
|
||||
T visitSupportsRule(CssSupportsRule node);
|
||||
}
|
||||
|
@ -20,5 +20,6 @@ abstract class StatementVisitor<T> {
|
||||
T visitReturn(Return node);
|
||||
T visitStyleRule(StyleRule node);
|
||||
T visitStylesheet(Stylesheet node);
|
||||
T visitSupportsRule(SupportsRule node);
|
||||
T visitVariableDeclaration(VariableDeclaration node);
|
||||
}
|
||||
|
@ -337,6 +337,60 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}, through: (node) => node is CssStyleRule, removeIfEmpty: true);
|
||||
}
|
||||
|
||||
void visitSupportsRule(SupportsRule node) {
|
||||
if (_declarationName != null) {
|
||||
throw node.span.message(
|
||||
"Supports rules may not be used within nested declarations.");
|
||||
}
|
||||
|
||||
var condition = new CssValue(
|
||||
_visitSupportsCondition(node.condition), node.condition.span);
|
||||
_withParent(new CssSupportsRule(condition, node.span), () {
|
||||
if (_selector == null) {
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
} else {
|
||||
// If we're in a style rule, copy it into the supports rule so that
|
||||
// declarations immediately inside @supports have somewhere to go.
|
||||
//
|
||||
// For example, "a {@supports (a: b) {b: c}}" should produce "@supports
|
||||
// (a: b) {a {b: c}}".
|
||||
_withParent(new CssStyleRule(_selector, _selector.span), () {
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
}, removeIfEmpty: true);
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
}
|
||||
|
||||
String _visitSupportsCondition(SupportsCondition condition) {
|
||||
if (condition is SupportsOperation) {
|
||||
return "${_parenthesize(condition.left, condition.operator)} "
|
||||
"${condition.operator} "
|
||||
"${_parenthesize(condition.right, condition.operator)}";
|
||||
} else if (condition is SupportsNegation) {
|
||||
return "not ${_parenthesize(condition.condition)}";
|
||||
} else if (condition is SupportsInterpolation) {
|
||||
return condition.expression.accept(this);
|
||||
} else if (condition is SupportsDeclaration) {
|
||||
return "(${condition.name.accept(this)}: ${condition.value.accept(this)})";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String _parenthesize(SupportsCondition condition, [String operator]) {
|
||||
if ((condition is SupportsNegation) ||
|
||||
(condition is SupportsOperation &&
|
||||
(operator == null || operator != condition.operator))) {
|
||||
return "(${_visitSupportsCondition(condition)})";
|
||||
} else {
|
||||
return _visitSupportsCondition(condition);
|
||||
}
|
||||
}
|
||||
|
||||
void visitVariableDeclaration(VariableDeclaration node) {
|
||||
_environment.setVariable(node.name, node.expression.accept(this),
|
||||
global: node.isGlobal);
|
||||
|
@ -42,14 +42,11 @@ String selectorToCss(Selector selector) {
|
||||
|
||||
class _SerializeCssVisitor
|
||||
implements CssVisitor, ValueVisitor, SelectorVisitor {
|
||||
final OutputStyle _style;
|
||||
|
||||
final _buffer = new StringBuffer();
|
||||
|
||||
var _indentation = 0;
|
||||
|
||||
_SerializeCssVisitor({OutputStyle style})
|
||||
: _style = style ?? OutputStyle.expanded;
|
||||
_SerializeCssVisitor({OutputStyle style});
|
||||
|
||||
void visitStylesheet(CssStylesheet node) {
|
||||
for (var child in node.children) {
|
||||
@ -134,6 +131,17 @@ class _SerializeCssVisitor
|
||||
_buffer.writeln();
|
||||
}
|
||||
|
||||
void visitSupportsRule(CssSupportsRule node) {
|
||||
_writeIndentation();
|
||||
_buffer.write("@supports ");
|
||||
_buffer.write(node.condition.value);
|
||||
_buffer.writeCharCode($space);
|
||||
_visitChildren(node.children);
|
||||
|
||||
// TODO: only add an extra newline if this is a group end
|
||||
_buffer.writeln();
|
||||
}
|
||||
|
||||
void visitDeclaration(CssDeclaration node) {
|
||||
_writeIndentation();
|
||||
_buffer.write(node.name.value);
|
||||
|
Loading…
x
Reference in New Issue
Block a user