@@ -197,6 +197,35 @@ export const parse = (
197197 } ) ;
198198 }
199199
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+
200229 /**
201230 * Parse selector.
202231 */
@@ -207,35 +236,54 @@ export const parse = (
207236 }
208237
209238 // remove comment in selector;
210- const res = trim ( m [ 0 ] ) . replace ( commentre , '' ) ;
239+ let res = trim ( m [ 0 ] ) . replace ( commentre , '' ) ;
211240
212241 // Optimisation: If there is no ',' no need to split or post-process (this is less costly)
213242 if ( res . indexOf ( ',' ) === - 1 ) {
214243 return [ res ] ;
215244 }
216245
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 ,
217285 return (
218286 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' ) )
239287 // Split the selector by ','
240288 . split ( ',' )
241289 // Replace back \u200C by ','
@@ -522,7 +570,7 @@ export const parse = (
522570 */
523571 function atcustommedia ( ) : CssCustomMediaAST | void {
524572 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 ] [ ^ { ; ] * ) ; / ) ;
526574 if ( ! m ) {
527575 return ;
528576 }
0 commit comments