@@ -93,6 +93,7 @@ public class ExportSwift {
9393var name : String ?
9494var cases : [ EnumCase ] = [ ]
9595var rawType : String ?
96+ var staticMethods : [ ExportedFunction ] = [ ]
9697}
9798var currentEnum = CurrentEnum ( )
9899
@@ -152,28 +153,54 @@ public class ExportSwift {
152153}
153154
154155override func visit( _ node: FunctionDeclSyntax ) -> SyntaxVisitorContinueKind {
156+ guard node. attributes. hasJSAttribute ( ) else {
157+ return . skipChildren
158+ }
159+
160+ let isStatic = node. modifiers. contains { modifierin
161+ modifier. name. tokenKind== . keyword( . static) ||
162+ modifier. name. tokenKind== . keyword( . class)
163+ }
164+
155165switch state{
156166case . topLevel:
157- if let exportedFunction= visitFunction (
158- node: node
159- ) {
167+ if isStatic{
168+ diagnose ( node: node, message: " Top-level functions cannot be static " )
169+ return . skipChildren
170+ }
171+ if let exportedFunction= visitFunction ( node: node, isStatic: false ) {
160172 exportedFunctions. append ( exportedFunction)
161173}
162174return . skipChildren
163- case . classBody( _ , let classKey) :
175+ case . classBody( let className , let classKey) :
164176if let exportedFunction= visitFunction (
165- node: node
177+ node: node,
178+ isStatic: isStatic,
179+ className: className,
180+ classKey: classKey
166181) {
167182exportedClassByName [ classKey] ? . methods. append ( exportedFunction)
168183}
169184return . skipChildren
170- case . enumBody:
171- diagnose ( node: node, message: " Functions are not supported inside enums " )
185+ case . enumBody( let enumName) :
186+ if !isStatic{
187+ diagnose ( node: node, message: " Only static functions are supported in enums " )
188+ return . skipChildren
189+ }
190+ if let exportedFunction= visitFunction ( node: node, isStatic: isStatic, enumName: enumName) {
191+ currentEnum. staticMethods. append ( exportedFunction)
192+ }
172193return . skipChildren
173194}
174195}
175196
176- private func visitFunction( node: FunctionDeclSyntax ) -> ExportedFunction ? {
197+ private func visitFunction(
198+ node: FunctionDeclSyntax ,
199+ isStatic: Bool ,
200+ className: String ? = nil ,
201+ classKey: String ? = nil ,
202+ enumName: String ? = nil
203+ ) -> ExportedFunction ? {
177204guard let jsAttribute= node. attributes. firstJSAttributeelse {
178205return nil
179206}
@@ -189,6 +216,14 @@ public class ExportSwift {
189216)
190217}
191218
219+ if namespace!= nil , case. enumBody= state{
220+ diagnose (
221+ node: jsAttribute,
222+ message: " Namespace is not supported for enum static functions " ,
223+ hint: " Remove the namespace from @JS attribute - enum functions inherit namespace from enum "
224+ )
225+ }
226+
192227var parameters : [ Parameter ] = [ ]
193228for param in node. signature. parameterClause. parameters{
194229let resolvedType = self . parent. lookupType ( for: param. type)
@@ -226,20 +261,52 @@ public class ExportSwift {
226261}
227262
228263let abiName : String
264+ let staticContext : StaticContext ?
265+
229266switch state{
230267case . topLevel:
231268 abiName= " bjs_ \( name) "
269+ staticContext= nil
232270case . classBody( let className, _) :
233- abiName= " bjs_ \( className) _ \( name) "
234- case . enumBody:
235- abiName= " "
236- diagnose (
237- node: node,
238- message: " Functions are not supported inside enums "
239- )
271+ if isStatic{
272+ abiName= " bjs_ \( className) _static_ \( name) "
273+ staticContext= . className( className)
274+ } else {
275+ abiName= " bjs_ \( className) _ \( name) "
276+ staticContext= nil
277+ }
278+ case . enumBody( let enumName) :
279+ if !isStatic{
280+ diagnose ( node: node, message: " Only static functions are supported in enums " )
281+ return nil
282+ }
283+
284+ let isNamespaceEnum = currentEnum. cases. isEmpty
285+
286+ if isNamespaceEnum{
287+ // For namespace enums, compute the full Swift call path manually
288+ var swiftPath : [ String ] = [ ]
289+ var currentNode : Syntax ? = node. parent
290+ while let parent= currentNode{
291+ if let enumDecl= parent. as ( EnumDeclSyntax . self) ,
292+ enumDecl. attributes. hasJSAttribute ( )
293+ {
294+ swiftPath. insert ( enumDecl. name. text, at: 0 )
295+ }
296+ currentNode= parent. parent
297+ }
298+ let fullEnumCallName = swiftPath. joined ( separator: " . " )
299+
300+ // ABI name should include full namespace path to avoid conflicts
301+ abiName= " bjs_ \( swiftPath. joined ( separator: " _ " ) ) _ \( name) "
302+ staticContext= . namespaceEnum( fullEnumCallName)
303+ } else {
304+ abiName= " bjs_ \( enumName) _static_ \( name) "
305+ staticContext= . enumName( enumName)
306+ }
240307}
241308
242- guard let effects= collectEffects ( signature: node. signature) else {
309+ guard let effects= collectEffects ( signature: node. signature, isStatic : isStatic ) else {
243310return nil
244311}
245312
@@ -249,11 +316,12 @@ public class ExportSwift {
249316 parameters: parameters,
250317 returnType: returnType,
251318 effects: effects,
252- namespace: namespace
319+ namespace: namespace,
320+ staticContext: staticContext
253321)
254322}
255323
256- private func collectEffects( signature: FunctionSignatureSyntax ) -> Effects ? {
324+ private func collectEffects( signature: FunctionSignatureSyntax , isStatic : Bool = false ) -> Effects ? {
257325let isAsync = signature. effectSpecifiers? . asyncSpecifier!= nil
258326var isThrows = false
259327if let throwsClause: ThrowsClauseSyntax = signature. effectSpecifiers? . throwsClause{
@@ -274,7 +342,7 @@ public class ExportSwift {
274342}
275343 isThrows= true
276344}
277- return Effects ( isAsync: isAsync, isThrows: isThrows)
345+ return Effects ( isAsync: isAsync, isThrows: isThrows, isStatic : isStatic )
278346}
279347
280348private func extractNamespace(
@@ -537,15 +605,25 @@ public class ExportSwift {
537605}
538606
539607let emitStyle = extractEnumStyle ( from: jsAttribute) ?? . const
540- if case. tsEnum= emitStyle,
541- let raw= currentEnum. rawType,
542- let rawEnum= SwiftEnumRawType . from ( raw) , rawEnum== . bool
543- {
544- diagnose (
545- node: jsAttribute,
546- message: " TypeScript enum style is not supported for Bool raw-value enums " ,
547- hint: " Use enumStyle: .const or change the raw type to String or a numeric type "
548- )
608+
609+ if case. tsEnum= emitStyle{
610+ if let raw= currentEnum. rawType,
611+ let rawEnum= SwiftEnumRawType . from ( raw) , rawEnum== . bool
612+ {
613+ diagnose (
614+ node: jsAttribute,
615+ message: " TypeScript enum style is not supported for Bool raw-value enums " ,
616+ hint: " Use enumStyle: .const or change the raw type to String or a numeric type "
617+ )
618+ }
619+
620+ if !currentEnum. staticMethods. isEmpty{
621+ diagnose (
622+ node: jsAttribute,
623+ message: " TypeScript enum style does not support static functions " ,
624+ hint: " Use enumStyle: .const to generate a const object that supports static functions "
625+ )
626+ }
549627}
550628
551629if currentEnum. cases. contains ( where: { !$0. associatedValues. isEmpty} ) {
@@ -597,7 +675,8 @@ public class ExportSwift {
597675 cases: currentEnum. cases,
598676 rawType: currentEnum. rawType,
599677 namespace: effectiveNamespace,
600- emitStyle: emitStyle
678+ emitStyle: emitStyle,
679+ staticMethods: currentEnum. staticMethods
601680)
602681exportedEnumByName [ enumName] = exportedEnum
603682 exportedEnumNames. append ( enumName)
@@ -862,6 +941,10 @@ public class ExportSwift {
862941case . namespace:
863942( )
864943}
944+
945+ for staticMethod in enumDef. staticMethods{
946+ decls. append ( try renderSingleExportedFunction ( function: staticMethod) )
947+ }
865948}
866949
867950for function in exportedFunctions{
@@ -1269,7 +1352,24 @@ public class ExportSwift {
12691352for param in function. parameters{
12701353try builder. liftParameter ( param: param)
12711354}
1272- builder. call ( name: function. name, returnType: function. returnType)
1355+
1356+ if function. effects. isStatic, let staticContext= function. staticContext{
1357+ let callName : String
1358+ switch staticContext{
1359+ case . className( let className) :
1360+ callName= " \( className) . \( function. name) "
1361+ case . enumName( let enumName) :
1362+ callName= " \( enumName) . \( function. name) "
1363+ case . namespaceEnum( let enumName) :
1364+ callName= " \( enumName) . \( function. name) "
1365+ case . explicitNamespace( let namespace) :
1366+ callName= " \( namespace. joined ( separator: " . " ) ) . \( function. name) "
1367+ }
1368+ builder. call ( name: callName, returnType: function. returnType)
1369+ } else {
1370+ builder. call ( name: function. name, returnType: function. returnType)
1371+ }
1372+
12731373try builder. lowerReturnValue ( returnType: function. returnType)
12741374return builder. render ( abiName: function. abiName)
12751375}
@@ -1335,17 +1435,25 @@ public class ExportSwift {
13351435}
13361436for method in klass. methods{
13371437let builder = ExportedThunkBuilder ( effects: method. effects)
1338- try builder. liftParameter (
1339- param: Parameter ( label: nil , name: " _self " , type: BridgeType . swiftHeapObject ( klass. swiftCallName) )
1340- )
1341- for param in method. parameters{
1342- try builder. liftParameter ( param: param)
1438+
1439+ if method. effects. isStatic{
1440+ for param in method. parameters{
1441+ try builder. liftParameter ( param: param)
1442+ }
1443+ builder. call ( name: " \( klass. swiftCallName) . \( method. name) " , returnType: method. returnType)
1444+ } else {
1445+ try builder. liftParameter (
1446+ param: Parameter ( label: nil , name: " _self " , type: BridgeType . swiftHeapObject ( klass. swiftCallName) )
1447+ )
1448+ for param in method. parameters{
1449+ try builder. liftParameter ( param: param)
1450+ }
1451+ builder. callMethod (
1452+ klassName: klass. swiftCallName,
1453+ methodName: method. name,
1454+ returnType: method. returnType
1455+ )
13431456}
1344- builder. callMethod (
1345- klassName: klass. swiftCallName,
1346- methodName: method. name,
1347- returnType: method. returnType
1348- )
13491457try builder. lowerReturnValue ( returnType: method. returnType)
13501458 decls. append ( builder. render ( abiName: method. abiName) )
13511459}