@@ -201,6 +201,7 @@ function convertStaticToDynamic(code) {
201201 // Process all navigators
202202 if ( navigatorInfos . length > 0 ) {
203203 const replacements = [ ] ;
204+ const navigatorConstNames = new Map ( ) ; // Track usage of navigator constant names
204205
205206 navigatorInfos . forEach ( ( navigatorInfo ) => {
206207 const {
@@ -223,7 +224,19 @@ function convertStaticToDynamic(code) {
223224 const withoutNavigator = withoutCreate . replace ( / N a v i g a t o r $ / , '' ) ; // "Stack"
224225 // Find the last capitalized word (e.g., "NativeStack" -> "Stack", "MaterialTopTab" -> "Tab")
225226 const match = withoutNavigator . match ( / ( [ A - Z ] [ a - z ] + ) $ / ) ;
226- const navigatorConstName = match ? match [ 1 ] : withoutNavigator ;
227+ const baseNavigatorConstName = match ? match [ 1 ] : withoutNavigator ;
228+
229+ // Handle multiple navigators of the same type by adding suffixes (A, B, C, etc.)
230+ let navigatorConstName = baseNavigatorConstName ;
231+ const currentCount = navigatorConstNames . get ( baseNavigatorConstName ) || 0 ;
232+
233+ if ( currentCount > 0 ) {
234+ // Add suffix: A for second occurrence, B for third, etc.
235+ const suffix = String . fromCharCode ( 65 + currentCount - 1 ) ; // 65 is 'A'
236+ navigatorConstName = baseNavigatorConstName + suffix ;
237+ }
238+
239+ navigatorConstNames . set ( baseNavigatorConstName , currentCount + 1 ) ;
227240
228241 // Parse the config object
229242 const parsedConfig = parseNavigatorConfig ( config ) ;
@@ -358,6 +371,7 @@ function convertStaticToDynamic(code) {
358371function parseNavigatorConfig ( configNode ) {
359372 const result = {
360373 screens : { } ,
374+ groups : { } , // Store groups
361375 navigatorProps : { } , // Store all navigator-level props
362376 } ;
363377
@@ -372,7 +386,79 @@ function parseNavigatorConfig(configNode) {
372386
373387 const keyName = prop . key . name || prop . key . value ;
374388
375- if ( keyName === 'screens' && t . isObjectExpression ( prop . value ) ) {
389+ if ( keyName === 'groups' && t . isObjectExpression ( prop . value ) ) {
390+ // Parse groups object
391+ prop . value . properties . forEach ( ( groupProp ) => {
392+ if ( ! t . isObjectProperty ( groupProp ) ) return ;
393+
394+ const groupKey = groupProp . key . name || groupProp . key . value ;
395+ const groupValue = groupProp . value ;
396+
397+ if ( t . isObjectExpression ( groupValue ) ) {
398+ const groupConfig = {
399+ screens : { } ,
400+ groupProps : { } ,
401+ } ;
402+
403+ groupValue . properties . forEach ( ( groupConfigProp ) => {
404+ if ( ! t . isObjectProperty ( groupConfigProp ) ) return ;
405+
406+ const configKey =
407+ groupConfigProp . key . name || groupConfigProp . key . value ;
408+
409+ if (
410+ configKey === 'screens' &&
411+ t . isObjectExpression ( groupConfigProp . value )
412+ ) {
413+ // Parse screens within the group
414+ groupConfigProp . value . properties . forEach ( ( screenProp ) => {
415+ if ( ! t . isObjectProperty ( screenProp ) ) return ;
416+
417+ const screenName = screenProp . key . name || screenProp . key . value ;
418+ const screenValue = screenProp . value ;
419+
420+ if ( t . isIdentifier ( screenValue ) ) {
421+ groupConfig . screens [ screenName ] = {
422+ component : screenValue . name ,
423+ screenProps : { } ,
424+ } ;
425+ } else if ( t . isObjectExpression ( screenValue ) ) {
426+ let component = null ;
427+ const screenProps = { } ;
428+
429+ screenValue . properties . forEach ( ( screenConfigProp ) => {
430+ if ( ! t . isObjectProperty ( screenConfigProp ) ) return ;
431+
432+ const key =
433+ screenConfigProp . key . name || screenConfigProp . key . value ;
434+
435+ if (
436+ key === 'screen' &&
437+ t . isIdentifier ( screenConfigProp . value )
438+ ) {
439+ component = screenConfigProp . value . name ;
440+ } else {
441+ screenProps [ key ] = screenConfigProp . value ;
442+ }
443+ } ) ;
444+
445+ groupConfig . screens [ screenName ] = { component, screenProps } ;
446+ }
447+ } ) ;
448+ } else {
449+ // Store other group-level props (screenOptions, screenLayout, etc.)
450+ groupConfig . groupProps [ configKey ] = {
451+ key : configKey ,
452+ value : groupConfigProp . value ,
453+ isStringLiteral : t . isStringLiteral ( groupConfigProp . value ) ,
454+ } ;
455+ }
456+ } ) ;
457+
458+ result . groups [ groupKey ] = groupConfig ;
459+ }
460+ } ) ;
461+ } else if ( keyName === 'screens' && t . isObjectExpression ( prop . value ) ) {
376462 // Parse screens object
377463 prop . value . properties . forEach ( ( screenProp ) => {
378464 if ( ! t . isObjectProperty ( screenProp ) ) return ;
@@ -455,6 +541,107 @@ function createNavigatorComponent(functionName, componentName, config) {
455541 // Create screen elements
456542 const screenElements = [ ] ;
457543
544+ // Handle groups if they exist
545+ if ( Object . keys ( config . groups ) . length > 0 ) {
546+ Object . entries ( config . groups ) . forEach ( ( [ groupKey , groupConfig ] ) => {
547+ const groupProps = [
548+ t . jsxAttribute (
549+ t . jsxIdentifier ( 'navigationKey' ) ,
550+ t . stringLiteral ( groupKey )
551+ ) ,
552+ ] ;
553+
554+ // Add group-level props (screenOptions, screenLayout, etc.)
555+ Object . values ( groupConfig . groupProps ) . forEach ( ( propInfo ) => {
556+ if ( propInfo . isStringLiteral ) {
557+ groupProps . push (
558+ t . jsxAttribute (
559+ t . jsxIdentifier ( propInfo . key ) ,
560+ t . stringLiteral ( propInfo . value . value )
561+ )
562+ ) ;
563+ } else {
564+ groupProps . push (
565+ t . jsxAttribute (
566+ t . jsxIdentifier ( propInfo . key ) ,
567+ t . jsxExpressionContainer ( propInfo . value )
568+ )
569+ ) ;
570+ }
571+ } ) ;
572+
573+ // Create screens for this group
574+ const groupScreenElements = [ ] ;
575+
576+ Object . entries ( groupConfig . screens ) . forEach (
577+ ( [ screenName , screenConfig ] ) => {
578+ const screenProps = [
579+ t . jsxAttribute (
580+ t . jsxIdentifier ( 'name' ) ,
581+ t . stringLiteral ( screenName )
582+ ) ,
583+ t . jsxAttribute (
584+ t . jsxIdentifier ( 'component' ) ,
585+ t . jsxExpressionContainer ( t . identifier ( screenConfig . component ) )
586+ ) ,
587+ ] ;
588+
589+ // Add all screen-level props
590+ Object . entries ( screenConfig . screenProps ) . forEach ( ( [ key , value ] ) => {
591+ screenProps . push (
592+ t . jsxAttribute (
593+ t . jsxIdentifier ( key ) ,
594+ t . jsxExpressionContainer ( value )
595+ )
596+ ) ;
597+ } ) ;
598+
599+ const screenElement = t . jsxElement (
600+ t . jsxOpeningElement (
601+ t . jsxMemberExpression (
602+ t . jsxIdentifier ( componentName ) ,
603+ t . jsxIdentifier ( 'Screen' )
604+ ) ,
605+ screenProps ,
606+ true
607+ ) ,
608+ null ,
609+ [ ] ,
610+ true
611+ ) ;
612+
613+ groupScreenElements . push ( t . jsxText ( '\n ' ) ) ;
614+ groupScreenElements . push ( screenElement ) ;
615+ }
616+ ) ;
617+
618+ groupScreenElements . push ( t . jsxText ( '\n ' ) ) ;
619+
620+ // Create the Group element
621+ const groupElement = t . jsxElement (
622+ t . jsxOpeningElement (
623+ t . jsxMemberExpression (
624+ t . jsxIdentifier ( componentName ) ,
625+ t . jsxIdentifier ( 'Group' )
626+ ) ,
627+ groupProps
628+ ) ,
629+ t . jsxClosingElement (
630+ t . jsxMemberExpression (
631+ t . jsxIdentifier ( componentName ) ,
632+ t . jsxIdentifier ( 'Group' )
633+ )
634+ ) ,
635+ groupScreenElements ,
636+ false
637+ ) ;
638+
639+ screenElements . push ( t . jsxText ( '\n ' ) ) ;
640+ screenElements . push ( groupElement ) ;
641+ } ) ;
642+ }
643+
644+ // Handle standalone screens (not in groups)
458645 Object . entries ( config . screens ) . forEach ( ( [ screenName , screenConfig ] ) => {
459646 const screenProps = [
460647 t . jsxAttribute ( t . jsxIdentifier ( 'name' ) , t . stringLiteral ( screenName ) ) ,
0 commit comments