Skip to content

Commit b98fa4f

Browse files
authored
Fix an edge case in superselector computation (#1866)
Closes #1843
1 parent 14c1634 commit b98fa4f

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
* Remove sourcemap comments from Sass sources. The generated sourcemap comment
44
for the compiled CSS output remains unaffected.
55

6+
* Fix a bug in `@extend` logic where certain selectors with three or more
7+
combinators were incorrectly considered superselectors of similar selectors
8+
with fewer combinators, causing them to be incorrectly trimmed from the
9+
output.
10+
611
* Produce a better error message for a number with a leading `+` or `-`, a
712
decimal point, but no digits.
813

lib/src/extend/functions.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ bool complexIsSuperselector(List<ComplexSelectorComponent> complex1,
598598

599599
var i1 = 0;
600600
var i2 = 0;
601+
Combinator? previousCombinator;
601602
while (true) {
602603
var remaining1 = complex1.length - i1;
603604
var remaining2 = complex2.length - i2;
@@ -643,6 +644,11 @@ bool complexIsSuperselector(List<ComplexSelectorComponent> complex1,
643644
parents.add(component2);
644645
}
645646

647+
if (!_compatibleWithPreviousCombinator(
648+
previousCombinator, parents ?? const [])) {
649+
return false;
650+
}
651+
646652
var component2 = complex2[endOfSubselector];
647653
var combinator1 = component1.combinators.firstOrNull;
648654
var combinator2 = component2.combinators.firstOrNull;
@@ -652,6 +658,7 @@ bool complexIsSuperselector(List<ComplexSelectorComponent> complex1,
652658

653659
i1++;
654660
i2 = endOfSubselector + 1;
661+
previousCombinator = combinator1;
655662

656663
if (complex1.length - i1 == 1) {
657664
if (combinator1 == Combinator.followingSibling) {
@@ -671,6 +678,25 @@ bool complexIsSuperselector(List<ComplexSelectorComponent> complex1,
671678
}
672679
}
673680

681+
/// Returns whether [parents] are valid intersitial components between one
682+
/// complex superselector and another, given that the earlier complex
683+
/// superselector had the combinator [previous].
684+
bool _compatibleWithPreviousCombinator(
685+
Combinator? previous, List<ComplexSelectorComponent> parents) {
686+
if (parents.isEmpty) return true;
687+
if (previous == null) return true;
688+
689+
// The child and next sibling combinators require that the *immediate*
690+
// following component be a superslector.
691+
if (previous != Combinator.followingSibling) return false;
692+
693+
// The following sibling combinator does allow intermediate components, but
694+
// only if they're all siblings.
695+
return parents.every((component) =>
696+
component.combinators.firstOrNull == Combinator.followingSibling ||
697+
component.combinators.firstOrNull == Combinator.nextSibling);
698+
}
699+
674700
/// Returns whether [combinator1] is a supercombinator of [combinator2].
675701
///
676702
/// That is, whether `X combinator1 Y` is a superselector of `X combinator2 Y`.

0 commit comments

Comments
 (0)