@@ -267,6 +267,24 @@ public class ExportSwift {
267267return namespaceString. split ( separator: " . " ) . map ( String . init)
268268}
269269
270+ private func extractEnumStyle(
271+ from jsAttribute: AttributeSyntax
272+ ) -> EnumEmitStyle ? {
273+ guard let arguments= jsAttribute. arguments? . as ( LabeledExprListSyntax . self) ,
274+ let styleArg= arguments. first ( where: { $0. label? . text== " enumStyle " } )
275+ else {
276+ return nil
277+ }
278+ let text = styleArg. expression. trimmedDescription
279+ if text. contains ( " tsEnum " ) {
280+ return . tsEnum
281+ }
282+ if text. contains ( " const " ) {
283+ return . const
284+ }
285+ return nil
286+ }
287+
270288override func visit( _ node: InitializerDeclSyntax ) -> SyntaxVisitorContinueKind {
271289guard node. attributes. hasJSAttribute ( ) else { return . skipChildren}
272290guard case. classBody( let className, _) = stateelse {
@@ -318,9 +336,6 @@ public class ExportSwift {
318336let name = node. name. text
319337
320338guard let jsAttribute= node. attributes. firstJSAttributeelse {
321- if case. enumBody( _) = state{
322- return . skipChildren
323- }
324339return . skipChildren
325340}
326341
@@ -355,14 +370,14 @@ public class ExportSwift {
355370}
356371
357372override func visitPost( _ node: ClassDeclSyntax ) {
358- stateStack. pop ( )
373+ // Make sure we pop the state stack only if we're in a class body state (meaning we successfully pushed)
374+ if case. classBody( _, _) = stateStack. current{
375+ stateStack. pop ( )
376+ }
359377}
360378
361379override func visit( _ node: EnumDeclSyntax ) -> SyntaxVisitorContinueKind {
362380guard node. attributes. hasJSAttribute ( ) else {
363- if case. enumBody( _) = state{
364- return . skipChildren
365- }
366381return . skipChildren
367382}
368383
@@ -402,6 +417,10 @@ public class ExportSwift {
402417guard let jsAttribute= node. attributes. firstJSAttribute,
403418let enumName= currentEnum. name
404419else {
420+ // Only pop if we have a valid enum that was processed
421+ if case. enumBody( _) = stateStack. current{
422+ stateStack. pop ( )
423+ }
405424return
406425}
407426
@@ -415,13 +434,26 @@ public class ExportSwift {
415434 effectiveNamespace= computedNamespace
416435}
417436
437+ let emitStyle = extractEnumStyle ( from: jsAttribute) ?? . const
438+ if case. tsEnum= emitStyle,
439+ let raw= currentEnum. rawType,
440+ let rawEnum= SwiftEnumRawType . from ( raw) , rawEnum== . bool
441+ {
442+ diagnose (
443+ node: jsAttribute,
444+ message: " TypeScript enum style is not supported for Bool raw-value enums " ,
445+ hint: " Use enumStyle: .const or change the raw type to String or a numeric type "
446+ )
447+ }
448+
418449let swiftCallName = ExportSwift . computeSwiftCallName ( for: node, itemName: enumName)
419450let exportedEnum = ExportedEnum (
420451 name: enumName,
421452 swiftCallName: swiftCallName,
422453 cases: currentEnum. cases,
423454 rawType: currentEnum. rawType,
424- namespace: effectiveNamespace
455+ namespace: effectiveNamespace,
456+ emitStyle: emitStyle
425457)
426458exportedEnumByName [ enumName] = exportedEnum
427459 exportedEnumNames. append ( enumName)
@@ -614,6 +646,11 @@ public class ExportSwift {
614646return nil
615647}
616648 decls. append ( Self . prelude)
649+
650+ for enumDef in exportedEnumswhere enumDef. enumType== . simple{
651+ decls. append ( renderCaseEnumHelpers ( enumDef) )
652+ }
653+
617654for function in exportedFunctions{
618655 decls. append ( renderSingleExportedFunction ( function: function) )
619656}
@@ -624,6 +661,32 @@ public class ExportSwift {
624661return decls. map { $0. formatted ( using: format) . description} . joined ( separator: " \n \n " )
625662}
626663
664+ func renderCaseEnumHelpers( _ enumDef: ExportedEnum ) -> DeclSyntax {
665+ let typeName = enumDef. swiftCallName
666+ var initCases : [ String ] = [ ]
667+ var valueCases : [ String ] = [ ]
668+ for (index, c) in enumDef. cases. enumerated ( ) {
669+ initCases. append ( " case \( index) : self = . \( c. name) " )
670+ valueCases. append ( " case . \( c. name) : return \( index) " )
671+ }
672+ let initSwitch = ( [ " switch bridgeJSRawValue { " ] + initCases+ [ " default: return nil " , " } " ] ) . joined (
673+ separator: " \n "
674+ )
675+ let valueSwitch = ( [ " switch self { " ] + valueCases+ [ " } " ] ) . joined ( separator: " \n " )
676+
677+ return """
678+ extension \( raw: typeName) {
679+ init?(bridgeJSRawValue: Int32) {
680+ \( raw: initSwitch)
681+ }
682+
683+ var bridgeJSRawValue: Int32 {
684+ \( raw: valueSwitch)
685+ }
686+ }
687+ """
688+ }
689+
627690class ExportedThunkBuilder {
628691var body : [ CodeBlockItemSyntax ] = [ ]
629692var abiParameterForwardings : [ LabeledExprSyntax ] = [ ]
@@ -700,7 +763,7 @@ public class ExportSwift {
700763 abiParameterForwardings. append (
701764LabeledExprSyntax (
702765 label: param. label,
703- expression: ExprSyntax ( " \( raw: enumName) (rawValue: Int( \( raw: param. name) ))! " )
766+ expression: ExprSyntax ( " \( raw: enumName) (bridgeJSRawValue: \( raw: param. name) )! " )
704767)
705768)
706769 abiParameterSignatures. append ( ( param. name, . i32) )
@@ -770,7 +833,6 @@ public class ExportSwift {
770833)
771834 abiParameterSignatures. append ( ( param. name, . i32) )
772835case . swiftHeapObject:
773- // UnsafeMutableRawPointer is passed as an i32 pointer
774836let objectExpr : ExprSyntax =
775837" Unmanaged< \( raw: param. type. swiftType) >.fromOpaque( \( raw: param. name) ).takeUnretainedValue() "
776838 abiParameterForwardings. append (
@@ -885,7 +947,7 @@ public class ExportSwift {
885947)
886948case . caseEnum:
887949 abiReturnType= . i32
888- append ( " returnInt32( ret.rawValue) " )
950+ append ( " return ret.bridgeJSRawValue " )
889951case . rawValueEnum( _, let rawType) :
890952if rawType== . string{
891953append (