@@ -533,13 +533,20 @@ export function syncRelation({
533533 sourceModel . fields . splice ( firstSourceFieldId , 0 , sourceFieldFactory . node ) ; // Insert the relation field before the first FK scalar field
534534
535535 const oppositeFieldPrefix = / [ 0 - 9 ] / g. test ( targetModel . name . charAt ( 0 ) ) ? '_' : '' ;
536- const { name : oppositeFieldName } = resolveNameCasing (
536+ let { name : oppositeFieldName } = resolveNameCasing (
537537 options . fieldCasing ,
538538 similarRelations > 0
539539 ? `${ oppositeFieldPrefix } ${ lowerCaseFirst ( sourceModel . name ) } _${ firstColumn } `
540540 : `${ lowerCaseFirst ( resolveNameCasing ( options . fieldCasing , sourceModel . name ) . name ) } ${ relation . references . type === 'many' ? 's' : '' } ` ,
541541 ) ;
542542
543+ if ( targetModel . fields . find ( ( f ) => f . name === oppositeFieldName ) ) {
544+ ( { name : oppositeFieldName } = resolveNameCasing (
545+ options . fieldCasing ,
546+ `${ lowerCaseFirst ( sourceModel . name ) } _${ firstColumn } To${ relation . references . table } _${ relation . references . columns [ 0 ] } ` ,
547+ ) ) ;
548+ }
549+
543550 const targetFieldFactory = new DataFieldFactory ( )
544551 . setContainer ( targetModel )
545552 . setName ( oppositeFieldName )
@@ -556,3 +563,116 @@ export function syncRelation({
556563
557564 targetModel . fields . push ( targetFieldFactory . node ) ;
558565}
566+
567+ /**
568+ * Consolidates per-column enums back to shared enums when possible.
569+ *
570+ * MySQL doesn't have named enum types — each column gets a synthetic enum
571+ * (e.g., `UserStatus`, `GroupStatus`). When the original schema used a shared
572+ * enum (e.g., `Status`) across multiple fields, this function detects the
573+ * mapping via field references and consolidates the synthetic enums back into
574+ * the original shared enum so the merge phase can match them correctly.
575+ */
576+ export function consolidateEnums ( {
577+ newModel,
578+ oldModel,
579+ } : {
580+ newModel : Model ;
581+ oldModel : Model ;
582+ } ) {
583+ const newEnums = newModel . declarations . filter ( ( d ) => isEnum ( d ) ) as Enum [ ] ;
584+ const newDataModels = newModel . declarations . filter ( ( d ) => d . $type === 'DataModel' ) as DataModel [ ] ;
585+ const oldDataModels = oldModel . declarations . filter ( ( d ) => d . $type === 'DataModel' ) as DataModel [ ] ;
586+
587+ // For each new enum, find which old enum it corresponds to (via field references)
588+ const enumMapping = new Map < Enum , Enum > ( ) ; // newEnum -> oldEnum
589+
590+ for ( const newEnum of newEnums ) {
591+ for ( const newDM of newDataModels ) {
592+ for ( const field of newDM . fields ) {
593+ if ( field . $type !== 'DataField' || field . type . reference ?. ref !== newEnum ) continue ;
594+
595+ // Find matching model in old model by db name
596+ const oldDM = oldDataModels . find ( ( d ) => getDbName ( d ) === getDbName ( newDM ) ) ;
597+ if ( ! oldDM ) continue ;
598+
599+ // Find matching field in old model by db name
600+ const oldField = oldDM . fields . find ( ( f ) => getDbName ( f ) === getDbName ( field ) ) ;
601+ if ( ! oldField || oldField . $type !== 'DataField' || ! oldField . type . reference ?. ref ) continue ;
602+
603+ const oldEnum = oldField . type . reference . ref ;
604+ if ( ! isEnum ( oldEnum ) ) continue ;
605+
606+ enumMapping . set ( newEnum , oldEnum as Enum ) ;
607+ break ;
608+ }
609+ if ( enumMapping . has ( newEnum ) ) break ;
610+ }
611+ }
612+
613+ // Group by old enum: oldEnum -> [newEnum1, newEnum2, ...]
614+ const reverseMapping = new Map < Enum , Enum [ ] > ( ) ;
615+ for ( const [ newEnum , oldEnum ] of enumMapping ) {
616+ if ( ! reverseMapping . has ( oldEnum ) ) {
617+ reverseMapping . set ( oldEnum , [ ] ) ;
618+ }
619+ reverseMapping . get ( oldEnum ) ! . push ( newEnum ) ;
620+ }
621+
622+ // Consolidate: when new enums map to the same old enum with matching values
623+ for ( const [ oldEnum , newEnumsGroup ] of reverseMapping ) {
624+ const keepEnum = newEnumsGroup [ 0 ] ! ;
625+
626+ // Skip if already correct (single enum with matching name)
627+ if ( newEnumsGroup . length === 1 && keepEnum . name === oldEnum . name ) continue ;
628+
629+ // Check that all new enums have the same values as the old enum
630+ const oldValues = new Set ( oldEnum . fields . map ( ( f ) => getDbName ( f ) ) ) ;
631+ const allMatch = newEnumsGroup . every ( ( ne ) => {
632+ const newValues = new Set ( ne . fields . map ( ( f ) => getDbName ( f ) ) ) ;
633+ return oldValues . size === newValues . size && [ ...oldValues ] . every ( ( v ) => newValues . has ( v ) ) ;
634+ } ) ;
635+
636+ if ( ! allMatch ) continue ;
637+
638+ // Rename the kept enum to match the old shared name
639+ keepEnum . name = oldEnum . name ;
640+
641+ // Replace keepEnum's attributes with those from the old enum so that
642+ // any synthetic @@map added by syncEnums is removed and getDbName(keepEnum)
643+ // reflects the consolidated name rather than the stale per-column name.
644+ // Shallow-copy and re-parent so AST $container pointers reference keepEnum.
645+ keepEnum . attributes = oldEnum . attributes . map ( ( attr ) => {
646+ const copy = { ...attr , $container : keepEnum } ;
647+ return copy ;
648+ } ) ;
649+
650+ // Remove duplicate enums from newModel
651+ for ( let i = 1 ; i < newEnumsGroup . length ; i ++ ) {
652+ const idx = newModel . declarations . indexOf ( newEnumsGroup [ i ] ! ) ;
653+ if ( idx >= 0 ) {
654+ newModel . declarations . splice ( idx , 1 ) ;
655+ }
656+ }
657+
658+ // Update all field references in newModel to point to the kept enum
659+ for ( const newDM of newDataModels ) {
660+ for ( const field of newDM . fields ) {
661+ if ( field . $type !== 'DataField' ) continue ;
662+ const ref = field . type . reference ?. ref ;
663+ if ( ref && newEnumsGroup . includes ( ref as Enum ) ) {
664+ ( field . type as any ) . reference = {
665+ ref : keepEnum ,
666+ $refText : keepEnum . name ,
667+ } ;
668+ }
669+ }
670+ }
671+
672+ console . log (
673+ colors . gray (
674+ `Consolidated enum${ newEnumsGroup . length > 1 ? 's' : '' } ${ newEnumsGroup . map ( ( e ) => e . name ) . join ( ', ' ) } → ${ oldEnum . name } ` ,
675+ ) ,
676+ ) ;
677+ }
678+ }
0 commit comments