1
1
/**
2
- * @typedef {import('./types.js ').Rule } Rule
3
- * @typedef {import('./types.js ').RuleAttr } RuleAttr
2
+ * @typedef {import('css-selector-parser ').AstAttribute } AstAttribute
3
+ * @typedef {import('css-selector-parser ').AstRule } AstRule
4
4
* @typedef {import('./types.js').Node } Node
5
5
*/
6
6
7
+ import { unreachable } from 'devlop'
7
8
import { zwitch } from 'zwitch'
8
9
9
- /** @type {(query: RuleAttr , node: Node) => boolean } */
10
+ /** @type {(query: AstAttribute , node: Node) => boolean } */
10
11
const handle = zwitch ( 'operator' , {
11
12
unknown : unknownOperator ,
12
13
// @ts -expect-error: hush.
@@ -21,15 +22,17 @@ const handle = zwitch('operator', {
21
22
} )
22
23
23
24
/**
24
- * @param {Rule } query
25
+ * @param {AstRule } query
25
26
* @param {Node } node
26
27
* @returns {boolean }
27
28
*/
28
29
export function attribute ( query , node ) {
29
30
let index = - 1
30
31
31
- while ( ++ index < query . attrs . length ) {
32
- if ( ! handle ( query . attrs [ index ] , node ) ) return false
32
+ if ( query . attributes ) {
33
+ while ( ++ index < query . attributes . length ) {
34
+ if ( ! handle ( query . attributes [ index ] , node ) ) return false
35
+ }
33
36
}
34
37
35
38
return true
@@ -40,7 +43,7 @@ export function attribute(query, node) {
40
43
*
41
44
* `[attr]`
42
45
*
43
- * @param {RuleAttr } query
46
+ * @param {AstAttribute } query
44
47
* @param {Node } node
45
48
* @returns {boolean }
46
49
*/
@@ -54,13 +57,15 @@ function exists(query, node) {
54
57
*
55
58
* `[attr=value]`
56
59
*
57
- * @param {RuleAttr } query
60
+ * @param {AstAttribute } query
58
61
* @param {Node } node
59
62
* @returns {boolean }
60
63
*/
61
64
function exact ( query , node ) {
65
+ const queryValue = attributeValue ( query )
66
+
62
67
// @ts -expect-error: Looks like a record.
63
- return exists ( query , node ) && String ( node [ query . name ] ) === query . value
68
+ return exists ( query , node ) && String ( node [ query . name ] ) === queryValue
64
69
}
65
70
66
71
/**
@@ -71,7 +76,7 @@ function exact(query, node) {
71
76
*
72
77
* `[attr~=value]`
73
78
*
74
- * @param {RuleAttr } query
79
+ * @param {AstAttribute } query
75
80
* @param {Node } node
76
81
* @returns {boolean }
77
82
*/
@@ -82,36 +87,39 @@ function containsArray(query, node) {
82
87
83
88
if ( value === null || value === undefined ) return false
84
89
85
- // If this is an array, and the query is contained in it, return true.
90
+ const queryValue = attributeValue ( query )
91
+
92
+ // If this is an array, and the query is contained in it, return `true`.
86
93
// Coverage comment in place because TS turns `Array.isArray(unknown)`
87
94
// into `Array<any>` instead of `Array<unknown>`.
88
95
// type-coverage:ignore-next-line
89
- if ( Array . isArray ( value ) && value . includes ( query . value ) ) {
96
+ if ( Array . isArray ( value ) && value . includes ( queryValue ) ) {
90
97
return true
91
98
}
92
99
93
100
// For all other values, return whether this is an exact match.
94
- return String ( value ) === query . value
101
+ return String ( value ) === queryValue
95
102
}
96
103
97
104
/**
98
105
* Check whether an attribute has a substring as its start.
99
106
*
100
107
* `[attr^=value]`
101
108
*
102
- * @param {RuleAttr } query
109
+ * @param {AstAttribute } query
103
110
* @param {Node } node
104
111
* @returns {boolean }
105
112
*/
106
113
function begins ( query , node ) {
107
114
/** @type {unknown } */
108
115
// @ts -expect-error: Looks like a record.
109
116
const value = node [ query . name ]
117
+ const queryValue = attributeValue ( query )
110
118
111
119
return Boolean (
112
120
query . value &&
113
121
typeof value === 'string' &&
114
- value . slice ( 0 , query . value . length ) === query . value
122
+ value . slice ( 0 , queryValue . length ) === queryValue
115
123
)
116
124
}
117
125
@@ -120,19 +128,20 @@ function begins(query, node) {
120
128
*
121
129
* `[attr$=value]`
122
130
*
123
- * @param {RuleAttr } query
131
+ * @param {AstAttribute } query
124
132
* @param {Node } node
125
133
* @returns {boolean }
126
134
*/
127
135
function ends ( query , node ) {
128
136
/** @type {unknown } */
129
137
// @ts -expect-error: Looks like a record.
130
138
const value = node [ query . name ]
139
+ const queryValue = attributeValue ( query )
131
140
132
141
return Boolean (
133
142
query . value &&
134
143
typeof value === 'string' &&
135
- value . slice ( - query . value . length ) === query . value
144
+ value . slice ( - queryValue . length ) === queryValue
136
145
)
137
146
}
138
147
@@ -141,16 +150,18 @@ function ends(query, node) {
141
150
*
142
151
* `[attr*=value]`
143
152
*
144
- * @param {RuleAttr } query
153
+ * @param {AstAttribute } query
145
154
* @param {Node } node
146
155
* @returns {boolean }
147
156
*/
148
157
function containsString ( query , node ) {
149
158
/** @type {unknown } */
150
159
// @ts -expect-error: Looks like a record.
151
160
const value = node [ query . name ]
161
+ const queryValue = attributeValue ( query )
162
+
152
163
return Boolean (
153
- query . value && typeof value === 'string' && value . includes ( query . value )
164
+ typeof value === 'string' && queryValue && value . includes ( queryValue )
154
165
)
155
166
}
156
167
@@ -164,3 +175,19 @@ function unknownOperator(query) {
164
175
// @ts -expect-error: `operator` guaranteed.
165
176
throw new Error ( 'Unknown operator `' + query . operator + '`' )
166
177
}
178
+
179
+ /**
180
+ * @param {AstAttribute } query
181
+ * @returns {string }
182
+ */
183
+ function attributeValue ( query ) {
184
+ const queryValue = query . value
185
+
186
+ /* c8 ignore next 4 -- never happens with our config */
187
+ if ( ! queryValue ) unreachable ( 'Attribute values should be defined' )
188
+ if ( queryValue . type === 'Substitution' ) {
189
+ unreachable ( 'Substitutions are not enabled' )
190
+ }
191
+
192
+ return queryValue . value
193
+ }
0 commit comments