4
4
5
5
import 'package:meta/meta.dart' ;
6
6
7
+ import '../evaluation_context.dart' ;
8
+ import '../exception.dart' ;
9
+ import '../visitor/any_selector.dart' ;
7
10
import '../visitor/interface/selector.dart' ;
8
11
import '../visitor/serialize.dart' ;
12
+ import 'selector/complex.dart' ;
13
+ import 'selector/list.dart' ;
14
+ import 'selector/placeholder.dart' ;
15
+ import 'selector/pseudo.dart' ;
9
16
10
17
export 'selector/attribute.dart' ;
11
18
export 'selector/class.dart' ;
19
+ export 'selector/combinator.dart' ;
12
20
export 'selector/complex.dart' ;
21
+ export 'selector/complex_component.dart' ;
13
22
export 'selector/compound.dart' ;
14
23
export 'selector/id.dart' ;
15
24
export 'selector/list.dart' ;
@@ -32,11 +41,131 @@ export 'selector/universal.dart';
32
41
abstract class Selector {
33
42
/// Whether this selector, and complex selectors containing it, should not be
34
43
/// emitted.
44
+ ///
45
+ /// @nodoc
35
46
@internal
36
- bool get isInvisible => false ;
47
+ bool get isInvisible => accept (const _IsInvisibleVisitor (includeBogus: true ));
48
+
49
+ // Whether this selector would be invisible even if it didn't have bogus
50
+ // combinators.
51
+ ///
52
+ /// @nodoc
53
+ @internal
54
+ bool get isInvisibleOtherThanBogusCombinators =>
55
+ accept (const _IsInvisibleVisitor (includeBogus: false ));
56
+
57
+ /// Whether this selector is not valid CSS.
58
+ ///
59
+ /// This includes both selectors that are useful exclusively for build-time
60
+ /// nesting (`> .foo)` and selectors with invalid combiantors that are still
61
+ /// supported for backwards-compatibility reasons (`.foo + ~ .bar` ).
62
+ bool get isBogus =>
63
+ accept (const _IsBogusVisitor (includeLeadingCombinator: true ));
64
+
65
+ /// Whether this selector is bogus other than having a leading combinator.
66
+ ///
67
+ /// @nodoc
68
+ @internal
69
+ bool get isBogusOtherThanLeadingCombinator =>
70
+ accept (const _IsBogusVisitor (includeLeadingCombinator: false ));
71
+
72
+ /// Whether this is a useless selector (that is, it's bogus _and_ it can't be
73
+ /// transformed into valid CSS by `@extend` or nesting).
74
+ ///
75
+ /// @nodoc
76
+ @internal
77
+ bool get isUseless => accept (const _IsUselessVisitor ());
78
+
79
+ /// Prints a warning if [this] is a bogus selector.
80
+ ///
81
+ /// This may only be called from within a custom Sass function. This will
82
+ /// throw a [SassScriptException] in Dart Sass 2.0.0.
83
+ void assertNotBogus ({String ? name}) {
84
+ if (! isBogus) return ;
85
+ warn (
86
+ (name == null ? '' : '\$ $name : ' ) +
87
+ '$this is not valid CSS.\n '
88
+ 'This will be an error in Dart Sass 2.0.0.\n '
89
+ '\n '
90
+ 'More info: https://sass-lang.com/d/bogus-combinators' ,
91
+ deprecation: true );
92
+ }
37
93
38
94
/// Calls the appropriate visit method on [visitor] .
39
95
T accept <T >(SelectorVisitor <T > visitor);
40
96
41
97
String toString () => serializeSelector (this , inspect: true );
42
98
}
99
+
100
+ /// The visitor used to implement [Selector.isInvisible] .
101
+ class _IsInvisibleVisitor extends AnySelectorVisitor {
102
+ /// Whether to consider selectors with bogus combinators invisible.
103
+ final bool includeBogus;
104
+
105
+ const _IsInvisibleVisitor ({required this .includeBogus});
106
+
107
+ bool visitSelectorList (SelectorList list) =>
108
+ list.components.every (visitComplexSelector);
109
+
110
+ bool visitComplexSelector (ComplexSelector complex) =>
111
+ super .visitComplexSelector (complex) ||
112
+ (includeBogus && complex.isBogusOtherThanLeadingCombinator);
113
+
114
+ bool visitPlaceholderSelector (PlaceholderSelector placeholder) => true ;
115
+
116
+ bool visitPseudoSelector (PseudoSelector pseudo) {
117
+ var selector = pseudo.selector;
118
+ if (selector == null ) return false ;
119
+
120
+ // We don't consider `:not(%foo)` to be invisible because, semantically, it
121
+ // means "doesn't match this selector that matches nothing", so it's
122
+ // equivalent to *. If the entire compound selector is composed of `:not`s
123
+ // with invisible lists, the serializer emits it as `*`.
124
+ return pseudo.name == 'not'
125
+ ? (includeBogus && selector.isBogus)
126
+ : selector.accept (this );
127
+ }
128
+ }
129
+
130
+ /// The visitor used to implement [Selector.isBogus] .
131
+ class _IsBogusVisitor extends AnySelectorVisitor {
132
+ /// Whether to consider selectors with leading combinators as bogus.
133
+ final bool includeLeadingCombinator;
134
+
135
+ const _IsBogusVisitor ({required this .includeLeadingCombinator});
136
+
137
+ bool visitComplexSelector (ComplexSelector complex) {
138
+ if (complex.components.isEmpty) {
139
+ return complex.leadingCombinators.isNotEmpty;
140
+ } else {
141
+ return complex.leadingCombinators.length >
142
+ (includeLeadingCombinator ? 0 : 1 ) ||
143
+ complex.components.last.combinators.isNotEmpty ||
144
+ complex.components.any ((component) =>
145
+ component.combinators.length > 1 ||
146
+ component.selector.accept (this ));
147
+ }
148
+ }
149
+
150
+ bool visitPseudoSelector (PseudoSelector pseudo) {
151
+ var selector = pseudo.selector;
152
+ if (selector == null ) return false ;
153
+
154
+ // The CSS spec specifically allows leading combinators in `:has()`.
155
+ return pseudo.name == 'has'
156
+ ? selector.isBogusOtherThanLeadingCombinator
157
+ : selector.isBogus;
158
+ }
159
+ }
160
+
161
+ /// The visitor used to implement [Selector.isUseless]
162
+ class _IsUselessVisitor extends AnySelectorVisitor {
163
+ const _IsUselessVisitor ();
164
+
165
+ bool visitComplexSelector (ComplexSelector complex) =>
166
+ complex.leadingCombinators.length > 1 ||
167
+ complex.components.any ((component) =>
168
+ component.combinators.length > 1 || component.selector.accept (this ));
169
+
170
+ bool visitPseudoSelector (PseudoSelector pseudo) => pseudo.isBogus;
171
+ }
0 commit comments