@@ -71,7 +71,7 @@ export default function componentNameAnnotatePlugin({ types: t }: typeof Babel):
71
71
enter ( path , state ) {
72
72
const fragmentContext = collectFragmentContext ( path ) ;
73
73
state . sentryFragmentContext = fragmentContext ;
74
- }
74
+ } ,
75
75
} ,
76
76
FunctionDeclaration ( path , state ) {
77
77
if ( ! path . node . id || ! path . node . id . name ) {
@@ -350,14 +350,17 @@ function applyAttributes(
350
350
) : void {
351
351
const [ componentAttributeName , elementAttributeName , sourceFileAttributeName ] = attributeNames ;
352
352
353
- if ( isReactFragment ( t , openingElement , fragmentContext ) ) {
354
- return ;
355
- }
356
353
// e.g., Raw JSX text like the `A` in `<h1>a</h1>`
357
354
if ( ! openingElement . node ) {
358
355
return ;
359
356
}
360
357
358
+ // Check if this is a React fragment - if so, skip attribute addition entirely
359
+ const isFragment = isReactFragment ( t , openingElement , fragmentContext ) ;
360
+ if ( isFragment ) {
361
+ return ;
362
+ }
363
+
361
364
if ( ! openingElement . node . attributes ) openingElement . node . attributes = [ ] ;
362
365
const elementName = getPathName ( t , openingElement ) ;
363
366
@@ -367,15 +370,11 @@ function applyAttributes(
367
370
368
371
// Add a stable attribute for the element name but only for non-DOM names
369
372
let isAnIgnoredElement = false ;
370
- if (
371
- ! isAnIgnoredComponent &&
372
- ! hasAttributeWithName ( openingElement , componentAttributeName ) &&
373
- ( componentAttributeName !== elementAttributeName || ! componentName )
374
- ) {
373
+ if ( ! isAnIgnoredComponent && ! hasAttributeWithName ( openingElement , elementAttributeName ) ) {
375
374
if ( DEFAULT_IGNORED_ELEMENTS . includes ( elementName ) ) {
376
375
isAnIgnoredElement = true ;
377
376
} else {
378
- // TODO: Is it possible to avoid this null check?
377
+ // Always add element attribute for non-ignored elements
379
378
if ( elementAttributeName ) {
380
379
openingElement . node . attributes . push (
381
380
t . jSXAttribute ( t . jSXIdentifier ( elementAttributeName ) , t . stringLiteral ( elementName ) )
@@ -390,22 +389,23 @@ function applyAttributes(
390
389
! isAnIgnoredComponent &&
391
390
! hasAttributeWithName ( openingElement , componentAttributeName )
392
391
) {
393
- // TODO: Is it possible to avoid this null check?
394
392
if ( componentAttributeName ) {
395
393
openingElement . node . attributes . push (
396
394
t . jSXAttribute ( t . jSXIdentifier ( componentAttributeName ) , t . stringLiteral ( componentName ) )
397
395
) ;
398
396
}
399
397
}
400
398
401
- // Add a stable attribute for the source file name (absent for non-root elements)
399
+ // Add a stable attribute for the source file name
400
+ // Updated condition: add source file for elements that have either:
401
+ // 1. A component name (root elements), OR
402
+ // 2. An element name that's not ignored (child elements)
402
403
if (
403
404
sourceFileName &&
404
405
! isAnIgnoredComponent &&
405
- ( componentName || isAnIgnoredElement === false ) &&
406
+ ( componentName || ! isAnIgnoredElement ) &&
406
407
! hasAttributeWithName ( openingElement , sourceFileAttributeName )
407
408
) {
408
- // TODO: Is it possible to avoid this null check?
409
409
if ( sourceFileAttributeName ) {
410
410
openingElement . node . attributes . push (
411
411
t . jSXAttribute ( t . jSXIdentifier ( sourceFileAttributeName ) , t . stringLiteral ( sourceFileName ) )
@@ -469,25 +469,25 @@ function attributeNamesFromState(state: AnnotationPluginPass): [string, string,
469
469
470
470
function collectFragmentContext ( programPath : Babel . NodePath ) : FragmentContext {
471
471
const fragmentAliases = new Set < string > ( ) ;
472
- const reactNamespaceAliases = new Set < string > ( [ ' React' ] ) ; // Default React namespace
473
-
472
+ const reactNamespaceAliases = new Set < string > ( [ " React" ] ) ; // Default React namespace
473
+
474
474
programPath . traverse ( {
475
475
ImportDeclaration ( importPath ) {
476
476
const source = importPath . node . source . value ;
477
-
477
+
478
478
// Handle React imports
479
- if ( source === ' react' || source === ' React' ) {
480
- importPath . node . specifiers . forEach ( spec => {
481
- if ( spec . type === ' ImportSpecifier' && spec . imported . type === ' Identifier' ) {
479
+ if ( source === " react" || source === " React" ) {
480
+ importPath . node . specifiers . forEach ( ( spec ) => {
481
+ if ( spec . type === " ImportSpecifier" && spec . imported . type === " Identifier" ) {
482
482
// import { Fragment } from 'react' -> Fragment
483
483
// import { Fragment as F } from 'react' -> F
484
- if ( spec . imported . name === ' Fragment' ) {
484
+ if ( spec . imported . name === " Fragment" ) {
485
485
fragmentAliases . add ( spec . local . name ) ;
486
486
}
487
- } else if ( spec . type === ' ImportDefaultSpecifier' ) {
487
+ } else if ( spec . type === " ImportDefaultSpecifier" ) {
488
488
// import React from 'react' -> React
489
489
reactNamespaceAliases . add ( spec . local . name ) ;
490
- } else if ( spec . type === ' ImportNamespaceSpecifier' ) {
490
+ } else if ( spec . type === " ImportNamespaceSpecifier" ) {
491
491
// import * as React from 'react' -> React
492
492
reactNamespaceAliases . add ( spec . local . name ) ;
493
493
}
@@ -499,52 +499,54 @@ function collectFragmentContext(programPath: Babel.NodePath): FragmentContext {
499
499
VariableDeclarator ( varPath ) {
500
500
if ( varPath . node . init ) {
501
501
const init = varPath . node . init ;
502
-
502
+
503
503
// Handle identifier assignments: const MyFragment = Fragment
504
- if ( varPath . node . id . type === ' Identifier' ) {
504
+ if ( varPath . node . id . type === " Identifier" ) {
505
505
// Handle: const MyFragment = Fragment (only if Fragment is a known alias)
506
- if ( init . type === ' Identifier' && fragmentAliases . has ( init . name ) ) {
506
+ if ( init . type === " Identifier" && fragmentAliases . has ( init . name ) ) {
507
507
fragmentAliases . add ( varPath . node . id . name ) ;
508
508
}
509
-
509
+
510
510
// Handle: const MyFragment = React.Fragment (only for known React namespaces)
511
- if ( init . type === 'MemberExpression' &&
512
- init . object . type === 'Identifier' &&
513
- init . property . type === 'Identifier' &&
514
- reactNamespaceAliases . has ( init . object . name ) &&
515
- init . property . name === 'Fragment' ) {
511
+ if (
512
+ init . type === "MemberExpression" &&
513
+ init . object . type === "Identifier" &&
514
+ init . property . type === "Identifier" &&
515
+ reactNamespaceAliases . has ( init . object . name ) &&
516
+ init . property . name === "Fragment"
517
+ ) {
516
518
fragmentAliases . add ( varPath . node . id . name ) ;
517
519
}
518
520
}
519
-
521
+
520
522
// Handle destructuring assignments: const { Fragment } = React
521
- if ( varPath . node . id . type === ' ObjectPattern' ) {
522
- if ( init . type === ' Identifier' && reactNamespaceAliases . has ( init . name ) ) {
523
+ if ( varPath . node . id . type === " ObjectPattern" ) {
524
+ if ( init . type === " Identifier" && reactNamespaceAliases . has ( init . name ) ) {
523
525
const properties = varPath . node . id . properties ;
524
-
526
+
525
527
for ( const prop of properties ) {
526
528
if (
527
- prop . type === ' ObjectProperty' &&
529
+ prop . type === " ObjectProperty" &&
528
530
prop . key &&
529
- prop . key . type === ' Identifier' &&
531
+ prop . key . type === " Identifier" &&
530
532
prop . value &&
531
- prop . value . type === ' Identifier' &&
532
- prop . key . name === ' Fragment'
533
+ prop . value . type === " Identifier" &&
534
+ prop . key . name === " Fragment"
533
535
) {
534
536
fragmentAliases . add ( prop . value . name ) ;
535
537
}
536
538
}
537
539
}
538
540
}
539
541
}
540
- }
542
+ } ,
541
543
} ) ;
542
544
543
545
return { fragmentAliases, reactNamespaceAliases } ;
544
546
}
545
547
546
548
function isReactFragment (
547
- t : typeof Babel . types ,
549
+ t : typeof Babel . types ,
548
550
openingElement : Babel . NodePath ,
549
551
context ?: FragmentContext // Add this optional parameter
550
552
) : boolean {
@@ -561,7 +563,7 @@ function isReactFragment(
561
563
}
562
564
563
565
// TODO: All these objects are typed as unknown, maybe an oversight in Babel types?
564
-
566
+
565
567
// Check if the element name is a known fragment alias
566
568
if ( context && elementName && context . fragmentAliases . has ( elementName ) ) {
567
569
return true ;
@@ -604,7 +606,10 @@ function isReactFragment(
604
606
// Enhanced checks using context
605
607
if ( context ) {
606
608
// Check React.Fragment pattern with known React namespaces
607
- if ( context . reactNamespaceAliases . has ( objectName as string ) && propertyName === "Fragment" ) {
609
+ if (
610
+ context . reactNamespaceAliases . has ( objectName as string ) &&
611
+ propertyName === "Fragment"
612
+ ) {
608
613
return true ;
609
614
}
610
615
0 commit comments