mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-22 22:02:00 +01:00
Ensure that selectors like :root always unify to the beginning (#1759)
Closes #1811
This commit is contained in:
parent
bc8df44f6a
commit
42d6fbb3ed
@ -1,3 +1,9 @@
|
||||
## 1.54.1
|
||||
|
||||
* When unifying selectors for `@extend` and `selector.unify()`, ensure that
|
||||
`:root`, `:scope`, `:host`, and `:host-context` only appear at the beginning
|
||||
of complex selectors.
|
||||
|
||||
## 1.54.0
|
||||
|
||||
* Deprecate selectors with leading or trailing combinators, or with multiple
|
||||
|
@ -18,6 +18,10 @@ import 'package:tuple/tuple.dart';
|
||||
import '../ast/selector.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
/// Pseudo-selectors that can only meaningfully appear in the first component of
|
||||
/// a complex selector.
|
||||
final _rootishPseudoClasses = {'root', 'scope', 'host', 'host-context'};
|
||||
|
||||
/// Returns the contents of a [SelectorList] that matches only elements that are
|
||||
/// matched by every complex selector in [complexes].
|
||||
///
|
||||
@ -231,19 +235,22 @@ Iterable<ComplexSelector>? _weaveParents(
|
||||
var trailingCombinators = _mergeTrailingCombinators(queue1, queue2);
|
||||
if (trailingCombinators == null) return null;
|
||||
|
||||
// Make sure there's at most one `:root` in the output.
|
||||
var root1 = _firstIfRoot(queue1);
|
||||
var root2 = _firstIfRoot(queue2);
|
||||
if (root1 != null && root2 != null) {
|
||||
var root =
|
||||
unifyCompound(root1.selector.components, root2.selector.components);
|
||||
if (root == null) return null;
|
||||
queue1.addFirst(ComplexSelectorComponent(root, root1.combinators));
|
||||
queue2.addFirst(ComplexSelectorComponent(root, root2.combinators));
|
||||
} else if (root1 != null) {
|
||||
queue2.addFirst(root1);
|
||||
} else if (root2 != null) {
|
||||
queue1.addFirst(root2);
|
||||
// Make sure all selectors that are required to be at the root
|
||||
var rootish1 = _firstIfRootish(queue1);
|
||||
var rootish2 = _firstIfRootish(queue2);
|
||||
if (rootish1 != null && rootish2 != null) {
|
||||
var rootish =
|
||||
unifyCompound(rootish1.selector.components, rootish2.selector.components);
|
||||
if (rootish == null) return null;
|
||||
queue1.addFirst(ComplexSelectorComponent(rootish, rootish1.combinators));
|
||||
queue2.addFirst(ComplexSelectorComponent(rootish, rootish2.combinators));
|
||||
} else if (rootish1 != null || rootish2 != null) {
|
||||
// If there's only one rootish selector, it should only appear in the first
|
||||
// position of the resulting selector. We can ensure that happens by adding
|
||||
// it to the beginning of _both_ queues.
|
||||
var rootish = (rootish1 ?? rootish2)!;
|
||||
queue1.addFirst(rootish);
|
||||
queue2.addFirst(rootish);
|
||||
}
|
||||
|
||||
var groups1 = _groupSelectors(queue1);
|
||||
@ -289,14 +296,19 @@ Iterable<ComplexSelector>? _weaveParents(
|
||||
];
|
||||
}
|
||||
|
||||
/// If the first element of [queue] has a `:root` selector, removes and returns
|
||||
/// that element.
|
||||
ComplexSelectorComponent? _firstIfRoot(Queue<ComplexSelectorComponent> queue) {
|
||||
/// If the first element of [queue] has a selector like `:root` that can only
|
||||
/// appear in a complex selector's first component, removes and returns that
|
||||
/// element.
|
||||
ComplexSelectorComponent? _firstIfRootish(Queue<ComplexSelectorComponent> queue) {
|
||||
if (queue.isEmpty) return null;
|
||||
var first = queue.first;
|
||||
if (!_hasRoot(first.selector)) return null;
|
||||
queue.removeFirst();
|
||||
return first;
|
||||
for (var simple in first.selector.components) {
|
||||
if (simple is PseudoSelector && simple.isClass && _rootishPseudoClasses.contains(simple.normalizedName)) {
|
||||
queue.removeFirst();
|
||||
return first;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns a leading combinator list that's compatible with both [combinators1]
|
||||
@ -543,12 +555,6 @@ QueueList<List<ComplexSelectorComponent>> _groupSelectors(
|
||||
return groups;
|
||||
}
|
||||
|
||||
/// Returns whether or not [compound] contains a `::root` selector.
|
||||
bool _hasRoot(CompoundSelector compound) => compound.components.any((simple) =>
|
||||
simple is PseudoSelector &&
|
||||
simple.isClass &&
|
||||
simple.normalizedName == 'root');
|
||||
|
||||
/// Returns whether [list1] is a superselector of [list2].
|
||||
///
|
||||
/// That is, whether [list1] matches every element that [list2] matches, as well
|
||||
|
@ -1,3 +1,7 @@
|
||||
## 2.0.1
|
||||
|
||||
* No user-visible changes.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Refactor the `CssMediaQuery` API to support new logical operators:
|
||||
|
@ -2,7 +2,7 @@ name: sass_api
|
||||
# Note: Every time we add a new Sass AST node, we need to bump the *major*
|
||||
# version because it's a breaking change for anyone who's implementing the
|
||||
# visitor interface(s).
|
||||
version: 2.0.0
|
||||
version: 2.0.1
|
||||
description: Additional APIs for Dart Sass.
|
||||
homepage: https://github.com/sass/dart-sass
|
||||
|
||||
@ -10,7 +10,7 @@ environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
sass: 1.54.0
|
||||
sass: 1.54.1
|
||||
|
||||
dev_dependencies:
|
||||
dartdoc: ^5.0.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: sass
|
||||
version: 1.54.0
|
||||
version: 1.54.1
|
||||
description: A Sass implementation in Dart.
|
||||
homepage: https://github.com/sass/dart-sass
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user