@@ -197,6 +197,35 @@ export const parse = (
197
197
} ) ;
198
198
}
199
199
200
+ function findClosingParenthese (
201
+ str : string ,
202
+ start : number ,
203
+ depth : number
204
+ ) : number {
205
+ let ptr = start + 1 ;
206
+ let found = false ;
207
+ let closeParentheses = str . indexOf ( ')' , ptr ) ;
208
+ while ( ! found && closeParentheses !== - 1 ) {
209
+ const nextParentheses = str . indexOf ( '(' , ptr ) ;
210
+ if ( nextParentheses !== - 1 && nextParentheses < closeParentheses ) {
211
+ const nextSearch = findClosingParenthese (
212
+ str ,
213
+ nextParentheses + 1 ,
214
+ depth + 1
215
+ ) ;
216
+ ptr = nextSearch + 1 ;
217
+ closeParentheses = str . indexOf ( ')' , ptr ) ;
218
+ } else {
219
+ found = true ;
220
+ }
221
+ }
222
+ if ( found && closeParentheses !== - 1 ) {
223
+ return closeParentheses ;
224
+ } else {
225
+ return - 1 ;
226
+ }
227
+ }
228
+
200
229
/**
201
230
* Parse selector.
202
231
*/
@@ -207,35 +236,54 @@ export const parse = (
207
236
}
208
237
209
238
// remove comment in selector;
210
- const res = trim ( m [ 0 ] ) . replace ( commentre , '' ) ;
239
+ let res = trim ( m [ 0 ] ) . replace ( commentre , '' ) ;
211
240
212
241
// Optimisation: If there is no ',' no need to split or post-process (this is less costly)
213
242
if ( res . indexOf ( ',' ) === - 1 ) {
214
243
return [ res ] ;
215
244
}
216
245
246
+ // Replace all the , in the parentheses by \u200C
247
+ let ptr = 0 ;
248
+ let startParentheses = res . indexOf ( '(' , ptr ) ;
249
+ while ( startParentheses !== - 1 ) {
250
+ const closeParentheses = findClosingParenthese ( res , startParentheses , 0 ) ;
251
+ if ( closeParentheses === - 1 ) {
252
+ break ;
253
+ }
254
+ ptr = closeParentheses + 1 ;
255
+ res =
256
+ res . substring ( 0 , startParentheses ) +
257
+ res
258
+ . substring ( startParentheses , closeParentheses )
259
+ . replace ( / , / g, '\u200C' ) +
260
+ res . substring ( closeParentheses ) ;
261
+ startParentheses = res . indexOf ( '(' , ptr ) ;
262
+ }
263
+
264
+ // Replace all the , in ' and " by \u200C
265
+ res = res
266
+ /**
267
+ * replace ',' by \u200C for data selector (div[data-lang="fr,de,us"])
268
+ *
269
+ * Examples:
270
+ * div[data-lang="fr,\"de,us"]
271
+ * div[data-lang='fr,\'de,us']
272
+ *
273
+ * Regex logic:
274
+ * ("|')(?:\\\1|.)*?\1 => Handle the " and '
275
+ *
276
+ * Optimization 1:
277
+ * No greedy capture (see docs about the difference between .* and .*?)
278
+ *
279
+ * Optimization 2:
280
+ * ("|')(?:\\\1|.)*?\1 this use reference to capture group, it work faster.
281
+ */
282
+ . replace ( / ( " | ' ) (?: \\ \1| .) * ?\1/ g, m => m . replace ( / , / g, '\u200C' ) ) ;
283
+
284
+ // Split all the left , and replace all the \u200C by ,
217
285
return (
218
286
res
219
- /**
220
- * replace ',' by \u200C for data selector (div[data-lang="fr,de,us"])
221
- * replace ',' by \u200C for nthChild and other selector (div:nth-child(2,3,4))
222
- *
223
- * Examples:
224
- * div[data-lang="fr,\"de,us"]
225
- * div[data-lang='fr,\'de,us']
226
- * div:matches(.toto, .titi:matches(.toto, .titi))
227
- *
228
- * Regex logic:
229
- * ("|')(?:\\\1|.)*?\1 => Handle the " and '
230
- * \(.*?\) => Handle the ()
231
- *
232
- * Optimization 1:
233
- * No greedy capture (see docs about the difference between .* and .*?)
234
- *
235
- * Optimization 2:
236
- * ("|')(?:\\\1|.)*?\1 this use reference to capture group, it work faster.
237
- */
238
- . replace ( / ( " | ' ) (?: \\ \1| .) * ?\1| \( .* ?\) / g, m => m . replace ( / , / g, '\u200C' ) )
239
287
// Split the selector by ','
240
288
. split ( ',' )
241
289
// Replace back \u200C by ','
@@ -522,7 +570,7 @@ export const parse = (
522
570
*/
523
571
function atcustommedia ( ) : CssCustomMediaAST | void {
524
572
const pos = position ( ) ;
525
- const m = match ( / ^ @ c u s t o m - m e d i a \s + ( - - [ ^ \s ] + ) \s * ( [ ^ { ; ] + ) ; / ) ;
573
+ const m = match ( / ^ @ c u s t o m - m e d i a \s + ( - - \S + ) \s * ( [ ^ { ; \s ] [ ^ { ; ] * ) ; / ) ;
526
574
if ( ! m ) {
527
575
return ;
528
576
}
0 commit comments