Support import clauses.

This commit is contained in:
Natalie Weizenbaum 2016-10-23 17:01:21 -07:00
parent 80055653d9
commit be8b26191d
5 changed files with 93 additions and 7 deletions

View File

@ -5,6 +5,7 @@
import 'package:source_span/source_span.dart';
import '../../visitor/interface/css.dart';
import 'media_query.dart';
import 'node.dart';
import 'value.dart';
@ -15,9 +16,16 @@ class CssImport extends CssNode {
/// This includes quotes.
final CssValue<String> url;
/// The supports condition attached to this import.
final CssValue<String> supports;
/// The media query attached to this import.
final List<CssMediaQuery> media;
final FileSpan span;
CssImport(this.url, this.span);
CssImport(this.url, this.span, {this.supports, Iterable<CssMediaQuery> media})
: media = media == null ? null : new List.unmodifiable(media);
/*=T*/ accept/*<T>*/(CssVisitor/*<T>*/ visitor) => visitor.visitImport(this);
}

View File

@ -2,11 +2,14 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'package:charcode/charcode.dart';
import 'package:source_span/source_span.dart';
import '../../../visitor/interface/statement.dart';
import '../interpolation.dart';
import '../media_query.dart';
import '../statement.dart';
import '../supports_condition.dart';
/// A rule that produces a plain CSS `@import` rule.
class PlainImportRule implements Statement {
@ -15,12 +18,28 @@ class PlainImportRule implements Statement {
/// This already contains quotes.
final Interpolation url;
/// The supports condition attached to this import, or `null` if no condition
/// is attached.
final SupportsCondition supports;
/// The media query attached to this import, or `null` if no condition is
/// attached.
final List<MediaQuery> media;
final FileSpan span;
PlainImportRule(this.url, this.span);
PlainImportRule(this.url, this.span,
{this.supports, Iterable<MediaQuery> media})
: media = media == null ? null : new List.unmodifiable(media);
/*=T*/ accept/*<T>*/(StatementVisitor/*<T>*/ visitor) =>
visitor.visitPlainImportRule(this);
String toString() => "@import $url;";
String toString() {
var buffer = new StringBuffer("@import $url");
if (supports != null) buffer.write(" supports($supports)");
if (media != null) buffer.write(" $media");
buffer.writeCharCode($semicolon);
return buffer.toString();
}
}

View File

@ -625,23 +625,29 @@ abstract class StylesheetParser extends Parser {
///
/// [start] should point before the `@`.
Statement _importRule(LineScannerState start) {
// TODO: parse supports clauses, url(), and query lists
var urlStart = scanner.state;
var next = scanner.peekChar();
if (next == $u || next == $U) {
var url = _dynamicUrl();
whitespace();
var queries = _tryImportQueries();
_expectStatementSeparator();
return new PlainImportRule(
new Interpolation([url], scanner.spanFrom(urlStart)),
scanner.spanFrom(start));
scanner.spanFrom(start),
supports: queries?.item1,
media: queries?.item2);
}
var url = string();
whitespace();
var queries = _tryImportQueries();
if (_isPlainImportUrl(url)) {
var interpolation = new Interpolation(
[scanner.substring(urlStart.position)], scanner.spanFrom(urlStart));
_expectStatementSeparator();
return new PlainImportRule(interpolation, scanner.spanFrom(start));
return new PlainImportRule(interpolation, scanner.spanFrom(start),
supports: queries?.item1, media: queries?.item2);
} else if (_inControlDirective || _inMixin) {
_disallowedAtRule(start);
return null;
@ -666,6 +672,38 @@ abstract class StylesheetParser extends Parser {
return url.startsWith("http://") || url.startsWith("https://");
}
/// Consumes a supports condition and/or a media query after an `@import`.
///
/// Returns `null` if neither type of query can be found.
Tuple2<SupportsCondition, List<MediaQuery>> _tryImportQueries() {
SupportsCondition supports;
if (scanIdentifier("supports", ignoreCase: true)) {
scanner.expectChar($lparen);
var start = scanner.state;
if (scanIdentifier("not", ignoreCase: true)) {
whitespace();
supports = new SupportsNegation(
_supportsConditionInParens(), scanner.spanFrom(start));
} else {
var name = _expression();
scanner.expectChar($colon);
whitespace();
var value = _expression();
supports =
new SupportsDeclaration(name, value, scanner.spanFrom(start));
}
scanner.expectChar($rparen);
whitespace();
}
var media =
_lookingAtInterpolatedIdentifier() || scanner.peekChar() == $lparen
? _mediaQueryList()
: null;
if (supports == null && media == null) return null;
return new Tuple2(supports, media);
}
/// Consumes an `@include` rule.
///
/// [start] should point before the `@`.

View File

@ -609,7 +609,17 @@ class _PerformVisitor
}
Value visitPlainImportRule(PlainImportRule node) {
_parent.addChild(new CssImport(_interpolationToValue(node.url), node.span));
var url = _interpolationToValue(node.url);
var supports = node.supports;
var resolvedSupports = supports is SupportsDeclaration
? "${supports.name.accept(this).toCssString()}: "
"${supports.value.accept(this).toCssString()})"
: (supports == null ? null : _visitSupportsCondition(supports));
_parent.addChild(new CssImport(url, node.span,
supports: resolvedSupports == null
? null
: new CssValue(resolvedSupports, node.supports.span),
media: node?.media?.map(_visitMediaQuery)));
return null;
}

View File

@ -141,6 +141,17 @@ class _SerializeCssVisitor
_writeIndentation();
_buffer.write("@import ");
_buffer.write(node.url.value);
if (node.supports != null) {
_buffer.writeCharCode($space);
_buffer.write(node.supports.value);
}
if (node.media != null) {
_buffer.writeCharCode($space);
_writeBetween(node.media, ', ', visitMediaQuery);
}
_buffer.writeCharCode($semicolon);
}