@@ -125,31 +125,61 @@ function withIdentifiers(styles) {
125
125
} )
126
126
}
127
127
128
+ let matchingBrackets = new Map ( [
129
+ [ '{' , '}' ] ,
130
+ [ '[' , ']' ] ,
131
+ [ '(' , ')' ] ,
132
+ ] )
133
+ let inverseMatchingBrackets = new Map (
134
+ Array . from ( matchingBrackets . entries ( ) ) . map ( ( [ k , v ] ) => [ v , k ] )
135
+ )
136
+
137
+ let quotes = new Set ( [ '"' , "'" , '`' ] )
138
+
139
+ // Arbitrary values must contain balanced brackets (), [] and {}. Escaped
140
+ // values don't count, and brackets inside quotes also don't count.
141
+ //
142
+ // E.g.: w-[this-is]w-[weird-and-invalid]
143
+ // E.g.: w-[this-is\\]w-\\[weird-but-valid]
144
+ // E.g.: content-['this-is-also-valid]-weirdly-enough']
128
145
function isValidArbitraryValue ( value ) {
129
- if ( value === undefined ) {
130
- return true
131
- }
146
+ let stack = [ ]
132
147
133
- // When an arbitrary value contans `[]` then it is probably invalid, unless
134
- // it is properly escaped. Or it is content that exists between quotes.
135
- // E.g.: w-[this-is]w-[weird-and-invalid]
136
- // E.g.: w-[this-is\\]w-\\[weird-but-valid]
137
- // E.g.: content-['this-is-also-valid]-weirdly-enough']
138
148
let inQuotes = false
139
149
for ( let i = 0 ; i < value . length ; i ++ ) {
140
- if ( [ '"' , "'" , '`' ] . includes ( value [ i ] ) ) {
150
+ let char = value [ i ]
151
+
152
+ // Non-escaped quotes allow us to "allow" anything in between
153
+ if ( quotes . has ( char ) && value [ i - 1 ] !== '\\' ) {
141
154
inQuotes = ! inQuotes
142
155
}
143
156
144
157
if ( inQuotes ) continue
158
+ if ( value [ i - 1 ] === '\\' ) continue // Escaped
159
+
160
+ if ( matchingBrackets . has ( char ) ) {
161
+ stack . push ( char )
162
+ } else if ( inverseMatchingBrackets . has ( char ) ) {
163
+ let inverse = inverseMatchingBrackets . get ( char )
164
+
165
+ // Nothing to pop from, therefore it is unbalanced
166
+ if ( stack . length <= 0 ) {
167
+ return false
168
+ }
145
169
146
- if ( value [ i ] === '[' || value [ i ] === ']' ) {
147
- if ( value [ i - 1 ] !== '\\' ) {
170
+ // Popped value must match the inverse value, otherwise it is unbalanced
171
+ if ( stack . pop ( ) !== inverse ) {
148
172
return false
149
173
}
150
174
}
151
175
}
152
176
177
+ // If there is still something on the stack, it is also unbalanced
178
+ if ( stack . length > 0 ) {
179
+ return false
180
+ }
181
+
182
+ // All good, totally balanced!
153
183
return true
154
184
}
155
185
@@ -297,11 +327,11 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
297
327
type = [ ] . concat ( type )
298
328
let [ value , coercedType ] = coerceValue ( type , modifier , options . values , tailwindConfig )
299
329
300
- if ( ! isValidArbitraryValue ( value ) ) {
330
+ if ( ! type . includes ( coercedType ) || value === undefined ) {
301
331
return [ ]
302
332
}
303
333
304
- if ( ! type . includes ( coercedType ) || value === undefined ) {
334
+ if ( ! isValidArbitraryValue ( value ) ) {
305
335
return [ ]
306
336
}
307
337
0 commit comments