Ensure that selectors like :root always unify to the beginning (#1759)

Closes #1811
This commit is contained in:
Natalie Weizenbaum 2022-08-02 16:37:08 -07:00 committed by GitHub
parent bc8df44f6a
commit 42d6fbb3ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 28 deletions

View File

@ -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

View File

@ -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,15 +296,20 @@ 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;
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]
/// and [combinators2].
@ -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

View File

@ -1,3 +1,7 @@
## 2.0.1
* No user-visible changes.
## 2.0.0
* Refactor the `CssMediaQuery` API to support new logical operators:

View File

@ -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

View File

@ -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