@@ -397,31 +397,48 @@ pub fn extract_style_from_expression<'a>(
397397 } else {
398398 match expression {
399399 Expression :: UnaryExpression ( un) => ExtractResult {
400+ // `name` is None only when this was reached through the `_xxx`
401+ // selector recursion (see line 324). In that case the value
402+ // cannot be statically extracted as a dynamic style because
403+ // the pseudo-selector has no CSS property slot to bind a
404+ // CSS variable to, so we return an empty result and let the
405+ // caller drop the attribute (see issue with `_hover={var}`).
400406 styles : if un. operator == UnaryOperator :: Void {
401407 vec ! [ ]
402- } else {
408+ } else if let Some ( name ) = name {
403409 vec ! [ dynamic_style(
404410 ast_builder,
405- name. unwrap ( ) ,
411+ name,
406412 expression,
407413 level,
408414 selector,
409415 ) ]
416+ } else {
417+ vec ! [ ]
410418 } ,
411419 ..ExtractResult :: default ( )
412420 } ,
421+ // Each variant is kept on its own line so per-line coverage
422+ // tools (tarpaulin on CI) can attribute the hit to the exact
423+ // pattern being exercised. The body is flattened to a single
424+ // `Option::map().unwrap_or_default()` chain to avoid an extra
425+ // if/else branch region — `name == None` happens only under
426+ // `_xxx={...}` pseudo-selector recursion, where no dynamic_style
427+ // can be emitted because the selector has no CSS property slot.
413428 Expression :: BinaryExpression ( _)
414429 | Expression :: StaticMemberExpression ( _)
415- | Expression :: CallExpression ( _) => ExtractResult {
416- styles : vec ! [ dynamic_style(
417- ast_builder,
418- name. unwrap( ) ,
419- expression,
420- level,
421- selector,
422- ) ] ,
423- ..ExtractResult :: default ( )
424- } ,
430+ | Expression :: CallExpression ( _) => name
431+ . map ( |name| ExtractResult {
432+ styles : vec ! [ dynamic_style(
433+ ast_builder,
434+ name,
435+ expression,
436+ level,
437+ selector,
438+ ) ] ,
439+ ..ExtractResult :: default ( )
440+ } )
441+ . unwrap_or_default ( ) ,
425442 Expression :: TSAsExpression ( exp) => extract_style_from_expression (
426443 ast_builder,
427444 name,
@@ -434,6 +451,11 @@ pub fn extract_style_from_expression<'a>(
434451 extract_style_from_member_expression ( ast_builder, name, mem, level, selector)
435452 }
436453 Expression :: TemplateLiteral ( _) => ExtractResult {
454+ // `typo == true` implies `name == Some("typography")` (set at
455+ // line 337 inside an `if let Some(name) = name` block), so the
456+ // typo branch is safe. The non-typo branch must handle the
457+ // `name.is_none()` case (pseudo-selector recursion) by
458+ // returning empty styles.
437459 styles : if typo {
438460 vec ! [ ExtractStyleProp :: Expression {
439461 expression: ast_builder. expression_template_literal(
@@ -463,22 +485,30 @@ pub fn extract_style_from_expression<'a>(
463485 ) ,
464486 styles: vec![ ] ,
465487 } ]
466- } else {
488+ } else if let Some ( name ) = name {
467489 vec ! [ dynamic_style(
468490 ast_builder,
469- name. unwrap ( ) ,
491+ name,
470492 expression,
471493 level,
472494 selector,
473495 ) ]
496+ } else {
497+ vec ! [ ]
474498 } ,
475499 ..ExtractResult :: default ( )
476500 } ,
477501 Expression :: Identifier ( identifier) => {
502+ // When `name` is `None` we are inside a pseudo-selector
503+ // recursion (e.g. `_hover={someIdentifier}`). In that case
504+ // the identifier is a black box (it may come from another
505+ // module) and we cannot statically extract a style from it,
506+ // so we skip extraction gracefully instead of panicking. The
507+ // pseudo-selector attribute will be stripped by the visitor
508+ // like any other non-extracted style prop.
478509 if IGNORED_IDENTIFIERS . contains ( & identifier. name . as_str ( ) ) {
479510 ExtractResult :: default ( )
480- } else {
481- let name = name. unwrap ( ) ;
511+ } else if let Some ( name) = name {
482512 if typo {
483513 ExtractResult {
484514 styles : vec ! [ ExtractStyleProp :: Expression {
@@ -530,6 +560,8 @@ pub fn extract_style_from_expression<'a>(
530560 ..ExtractResult :: default ( )
531561 }
532562 }
563+ } else {
564+ ExtractResult :: default ( )
533565 }
534566 }
535567 Expression :: LogicalExpression ( logical) => {
0 commit comments