@@ -268,21 +268,22 @@ def _module_css_definitions(content: str, rel_path: str):
268268# delimiter parsing separate from strings inside JavaScript expressions.
269269_JS_LITERAL_PATTERN = re .compile (r"""'(?:\\[\s\S]|[^'\\\r\n])*'|"(?:\\[\s\S]|[^"\\\r\n])*"|`(?:\\[\s\S]|[^`\\])*`""" )
270270_JS_COMMENT_PATTERN = re .compile (r"//[^\r\n]*|/\*[\s\S]*?\*/" )
271- _JSX_TAG_PATTERN = re .compile (r"<(?:[^\W\d]|\$)[\w$:.-]*" )
272- _JSX_ATTRIBUTE_PATTERN = re .compile (r"[a-zA-Z_:] [\w:.-]*" )
271+ _JSX_TAG_PATTERN = re .compile (r"<(?:[^\W\d]|\$)[\w$:.-]*(?=[\s/>]) " )
272+ _JSX_ATTRIBUTE_PATTERN = re .compile (r"(?:[^\W\d]|[$:]) [\w$ :.-]*" )
273273_CLASS_PROPERTY_PATTERN = re .compile (r"""(?<![\w$])(?:className|class|['"]className['"]|['"]class['"])\s*:\s*""" )
274274_CLASSNAMES_CALL_PATTERN = re .compile (r"(?<![\w$])(?:classNames|clsx)\s*(?=\()" )
275275_JSX_ENTITY_PATTERN = re .compile (r"&(?:#[xX][0-9a-fA-F]+|#[0-9]+|[a-zA-Z][a-zA-Z0-9]*);" )
276276_HTML_CLASS_SPACE_PATTERN = re .compile (r"[ \t\r\n\f]+" )
277277
278278
279- def _js_delimited_end (content : str , start : int ) -> int | None :
279+ def _js_delimited_end (content : str , start : int , * , limit : int | None = None ) -> int | None :
280280 """Find a closing JS delimiter without treating quoted braces as syntax."""
281281 closing = {"{" : "}" , "[" : "]" , "(" : ")" }
282282 stack = [closing [content [start ]]]
283283 index = start + 1
284- while index < len (content ):
285- skipped = _JS_LITERAL_PATTERN .match (content , index ) or _JS_COMMENT_PATTERN .match (content , index )
284+ limit = len (content ) if limit is None else limit
285+ while index < limit :
286+ skipped = _JS_LITERAL_PATTERN .match (content , index , limit ) or _JS_COMMENT_PATTERN .match (content , index , limit )
286287 if skipped :
287288 index = skipped .end ()
288289 continue
@@ -356,42 +357,42 @@ def _jsx_class_attributes(content: str, attribute_positions: set[int] | None = N
356357 """
357358 for tag in _JSX_TAG_PATTERN .finditer (content ):
358359 index = tag .end ()
359- while index < len (content ):
360+ limit = min (len (content ), tag .start () + 8192 )
361+ while index < limit :
360362 if content [index ].isspace () or content [index ] == "/" :
361363 index += 1
362364 continue
363365 if content [index ] == ">" :
364366 break
365367 if content [index ] == "{" :
366- end = _js_delimited_end (content , index )
368+ end = _js_delimited_end (content , index , limit = limit )
367369 if end is None :
368370 yield False , None
369371 break
370372 if content [index + 1 : end - 1 ].lstrip ().startswith ("..." ):
371373 yield False , None # Spread props may supply any global class.
372374 index = end
373375 continue
374- name = _JSX_ATTRIBUTE_PATTERN .match (content , index )
376+ name = _JSX_ATTRIBUTE_PATTERN .match (content , index , limit )
375377 if name is None :
376- index += 1
377- continue
378+ break # Not an attribute: do not walk the remaining file per candidate.
378379 wanted = name .group () in {"className" , "class" }
379380 if wanted and attribute_positions is not None :
380381 attribute_positions .add (name .start ())
381382 index = name .end ()
382- while index < len ( content ) and content [index ].isspace ():
383+ while index < limit and content [index ].isspace ():
383384 index += 1
384- if index >= len ( content ) or content [index ] != "=" :
385+ if index >= limit or content [index ] != "=" :
385386 continue
386387 index += 1
387- while index < len ( content ) and content [index ].isspace ():
388+ while index < limit and content [index ].isspace ():
388389 index += 1
389- if index == len ( content ) :
390+ if index == limit :
390391 if wanted :
391392 yield False , None
392393 break
393394 if content [index ] in "'\" " :
394- end = content .find (content [index ], index + 1 )
395+ end = content .find (content [index ], index + 1 , limit )
395396 if end < 0 :
396397 if wanted :
397398 yield False , None
@@ -400,7 +401,7 @@ def _jsx_class_attributes(content: str, attribute_positions: set[int] | None = N
400401 yield True , content [index + 1 : end ]
401402 index = end + 1
402403 elif content [index ] == "{" :
403- end = _js_delimited_end (content , index )
404+ end = _js_delimited_end (content , index , limit = limit )
404405 if wanted :
405406 yield False , content [index + 1 : end - 1 ] if end is not None else None
406407 if end is None :
@@ -410,6 +411,8 @@ def _jsx_class_attributes(content: str, attribute_positions: set[int] | None = N
410411 if wanted :
411412 yield False , None
412413 index += 1
414+ if index >= limit and limit < len (content ):
415+ yield False , None # An oversized header leaves possible global uses uncertain.
413416
414417
415418# tsconfig paths for path aliases
0 commit comments