Skip to content

Commit

Permalink
Add support for arbitrary modifiers after @import
Browse files Browse the repository at this point in the history
  • Loading branch information
nex3 committed May 18, 2022
1 parent 2e7db70 commit 63fe404
Show file tree
Hide file tree
Showing 22 changed files with 208 additions and 129 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
@@ -1,7 +1,13 @@
## 1.51.1
## 1.52.0

* Add support for arbitrary modifiers at the end of plain CSS imports, in
addition to the existing `supports()` and media queries. Sass now allows any
sequence of identifiers of functions after the URL of an import for forwards
compatibility with future additions to the CSS spec.

* Fix an issue where source locations tracked through variable references could
potentially become incorrect.

* Fix a bug where a loud comment in the source can break the source map when
embedding the sources, when using the command-line interface or the legacy JS
API.
Expand Down
8 changes: 2 additions & 6 deletions lib/src/ast/css/import.dart
Expand Up @@ -3,7 +3,6 @@
// https://opensource.org/licenses/MIT.

import '../../visitor/interface/css.dart';
import 'media_query.dart';
import 'node.dart';
import 'value.dart';

Expand All @@ -14,11 +13,8 @@ abstract class CssImport extends CssNode {
/// This includes quotes.
CssValue<String> get url;

/// The supports condition attached to this import.
CssValue<String>? get supports;

/// The media query attached to this import.
List<CssMediaQuery>? get media;
/// The modifiers (such as media or supports queries) attached to this import.
CssValue<String>? get modifiers;

T accept<T>(CssVisitor<T> visitor) => visitor.visitCssImport(this);
}
11 changes: 2 additions & 9 deletions lib/src/ast/css/modifiable/import.dart
Expand Up @@ -6,7 +6,6 @@ import 'package:source_span/source_span.dart';

import '../../../visitor/interface/modifiable_css.dart';
import '../import.dart';
import '../media_query.dart';
import '../value.dart';
import 'node.dart';

Expand All @@ -17,17 +16,11 @@ class ModifiableCssImport extends ModifiableCssNode implements CssImport {
/// 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 CssValue<String>? modifiers;

final FileSpan span;

ModifiableCssImport(this.url, this.span,
{this.supports, Iterable<CssMediaQuery>? media})
: media = media == null ? null : List.unmodifiable(media);
ModifiableCssImport(this.url, this.span, {this.modifiers});

T accept<T>(ModifiableCssVisitor<T> visitor) => visitor.visitCssImport(this);
}
20 changes: 5 additions & 15 deletions lib/src/ast/sass/import/static.dart
Expand Up @@ -7,7 +7,6 @@ import 'package:source_span/source_span.dart';

import '../import.dart';
import '../interpolation.dart';
import '../supports_condition.dart';

/// An import that produces a plain CSS `@import` rule.
///
Expand All @@ -19,22 +18,13 @@ class StaticImport implements Import {
/// 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 Interpolation? media;
/// The modifiers (such as media or supports queries) attached to this import,
/// or `null` if none are attached.
final Interpolation? modifiers;

final FileSpan span;

StaticImport(this.url, this.span, {this.supports, this.media});
StaticImport(this.url, this.span, {this.modifiers});

String toString() {
var buffer = StringBuffer(url);
if (supports != null) buffer.write(" supports($supports)");
if (media != null) buffer.write(" $media");
return buffer.toString();
}
String toString() => "$url${modifiers == null ? '' : ' $modifiers'}";
}
23 changes: 23 additions & 0 deletions lib/src/ast/sass/interpolation.dart
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';
import 'package:source_span/source_span.dart';

import '../../interpolation_buffer.dart';
import 'expression.dart';
import 'node.dart';

Expand Down Expand Up @@ -40,6 +41,28 @@ class Interpolation implements SassNode {
return first is String ? first : '';
}

/// Creates a new [Interpolation] by concatenating a sequence of [String]s,
/// [Expression]s, or nested [Interpolation]s.
static Interpolation concat(
Iterable<Object /* String | Expression | Interpolation */ > contents,
FileSpan span) {
var buffer = InterpolationBuffer();
for (var element in contents) {
if (element is String) {
buffer.write(element);
} else if (element is Expression) {
buffer.add(element);
} else if (element is Interpolation) {
buffer.addInterpolation(element);
} else {
throw ArgumentError.value(contents, "contents",
"May only contains Strings, Expressions, or Interpolations.");
}
}

return buffer.interpolation(span);
}

Interpolation(Iterable<Object /* String | Expression */ > contents, this.span)
: contents = List.unmodifiable(contents) {
for (var i = 0; i < this.contents.length; i++) {
Expand Down
7 changes: 6 additions & 1 deletion lib/src/ast/sass/supports_condition.dart
Expand Up @@ -4,10 +4,15 @@

import 'package:meta/meta.dart';

import 'interpolation.dart';
import 'node.dart';

/// An abstract class for defining the condition a `@supports` rule selects.
///
/// {@category AST}
@sealed
abstract class SupportsCondition extends SassNode {}
abstract class SupportsCondition extends SassNode {
/// Returns an [Interpolation] that evaluates to the same value as this
/// [SupportsCondition].
Interpolation toInterpolation();
}
3 changes: 3 additions & 0 deletions lib/src/ast/sass/supports_condition/anything.dart
Expand Up @@ -21,5 +21,8 @@ class SupportsAnything implements SupportsCondition {

SupportsAnything(this.contents, this.span);

Interpolation toInterpolation() =>
Interpolation.concat(['(', contents, ')'], span);

String toString() => "($contents)";
}
4 changes: 4 additions & 0 deletions lib/src/ast/sass/supports_condition/declaration.dart
Expand Up @@ -7,6 +7,7 @@ import 'package:source_span/source_span.dart';

import '../expression.dart';
import '../expression/string.dart';
import '../interpolation.dart';
import '../supports_condition.dart';

/// A condition that selects for browsers where a given declaration is
Expand Down Expand Up @@ -42,5 +43,8 @@ class SupportsDeclaration implements SupportsCondition {

SupportsDeclaration(this.name, this.value, this.span);

Interpolation toInterpolation() => Interpolation.concat(
['(', name, isCustomProperty ? ':' : ': ', value, ')'], span);

String toString() => "($name: $value)";
}
3 changes: 3 additions & 0 deletions lib/src/ast/sass/supports_condition/function.dart
Expand Up @@ -23,5 +23,8 @@ class SupportsFunction implements SupportsCondition {

SupportsFunction(this.name, this.arguments, this.span);

Interpolation toInterpolation() =>
Interpolation.concat([name, '(', arguments, ')'], span);

String toString() => "$name($arguments)";
}
3 changes: 3 additions & 0 deletions lib/src/ast/sass/supports_condition/interpolation.dart
Expand Up @@ -6,6 +6,7 @@ import 'package:meta/meta.dart';
import 'package:source_span/source_span.dart';

import '../expression.dart';
import '../interpolation.dart';
import '../supports_condition.dart';

/// An interpolated condition.
Expand All @@ -20,5 +21,7 @@ class SupportsInterpolation implements SupportsCondition {

SupportsInterpolation(this.expression, this.span);

Interpolation toInterpolation() => Interpolation([expression], span);

String toString() => "#{$expression}";
}
12 changes: 12 additions & 0 deletions lib/src/ast/sass/supports_condition/negation.dart
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';
import 'package:source_span/source_span.dart';

import '../interpolation.dart';
import '../supports_condition.dart';
import 'operation.dart';

Expand All @@ -20,6 +21,17 @@ class SupportsNegation implements SupportsCondition {

SupportsNegation(this.condition, this.span);

Interpolation toInterpolation() {
var needsParens =
condition is SupportsNegation || condition is SupportsOperation;
return Interpolation.concat([
"not ",
if (needsParens) "(",
condition.toInterpolation(),
if (needsParens) ")"
], span);
}

String toString() {
if (condition is SupportsNegation || condition is SupportsOperation) {
return "not ($condition)";
Expand Down
20 changes: 18 additions & 2 deletions lib/src/ast/sass/supports_condition/operation.dart
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';
import 'package:source_span/source_span.dart';

import '../interpolation.dart';
import '../supports_condition.dart';
import 'negation.dart';

Expand Down Expand Up @@ -34,10 +35,25 @@ class SupportsOperation implements SupportsCondition {
}
}

Interpolation toInterpolation() => Interpolation.concat([
..._parenthesizeInterpolation(left),
" $operator ",
..._parenthesizeInterpolation(right)
], span);

/// Returns a list that can be passed to [Interpolation.concat], with
/// parentheses around [condition] if necessary.
List<Object /* String | Expression | Interpolation */ >
_parenthesizeInterpolation(SupportsCondition condition) => condition
is SupportsNegation ||
(condition is SupportsOperation && condition.operator == operator)
? ["(", condition.toInterpolation(), ")"]
: [condition.toInterpolation()];

String toString() =>
"${_parenthesize(left)} $operator ${_parenthesize(right)}";
"${_parenthesizeString(left)} $operator ${_parenthesizeString(right)}";

String _parenthesize(SupportsCondition condition) =>
String _parenthesizeString(SupportsCondition condition) =>
condition is SupportsNegation ||
(condition is SupportsOperation && condition.operator == operator)
? "($condition)"
Expand Down
4 changes: 2 additions & 2 deletions lib/src/parse/css.dart
Expand Up @@ -94,11 +94,11 @@ class CssParser extends ScssParser {
var urlSpan = scanner.spanFrom(urlStart);

whitespace();
var queries = tryImportQueries();
var modifiers = tryImportModifiers();
expectStatementSeparator("@import rule");
return ImportRule([
StaticImport(Interpolation([url], urlSpan), scanner.spanFrom(urlStart),
supports: queries?.item1, media: queries?.item2)
modifiers: modifiers)
], scanner.spanFrom(start));
}

Expand Down

0 comments on commit 63fe404

Please sign in to comment.