@@ -1743,6 +1743,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17431743 // Current type being collected (when in jsClassBody state)
17441744 private struct CurrentType {
17451745 let name : String
1746+ let jsName : String ?
17461747 var constructor : ImportedConstructorSkeleton ?
17471748 var methods : [ ImportedFunctionSkeleton ]
17481749 var getters : [ ImportedGetterSkeleton ]
@@ -1758,10 +1759,22 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17581759 hasAttribute ( attributes , name : " JSFunction " )
17591760 }
17601761
1762+ static func firstJSFunctionAttribute ( _ attributes : AttributeListSyntax ? ) -> AttributeSyntax ? {
1763+ attributes ? . first { attribute in
1764+ attribute . as ( AttributeSyntax . self ) ? . attributeName . trimmedDescription == " JSFunction "
1765+ } ? . as ( AttributeSyntax . self )
1766+ }
1767+
17611768 static func hasJSGetterAttribute ( _ attributes : AttributeListSyntax ? ) -> Bool {
17621769 hasAttribute ( attributes , name : " JSGetter " )
17631770 }
17641771
1772+ static func firstJSGetterAttribute ( _ attributes : AttributeListSyntax ? ) -> AttributeSyntax ? {
1773+ attributes ? . first { attribute in
1774+ attribute . as ( AttributeSyntax . self ) ? . attributeName . trimmedDescription == " JSGetter "
1775+ } ? . as ( AttributeSyntax . self )
1776+ }
1777+
17651778 static func hasJSSetterAttribute ( _ attributes : AttributeListSyntax ? ) -> Bool {
17661779 hasAttribute ( attributes , name : " JSSetter " )
17671780 }
@@ -1776,6 +1789,12 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17761789 hasAttribute ( attributes , name : " JSClass " )
17771790 }
17781791
1792+ static func firstJSClassAttribute ( _ attributes : AttributeListSyntax ? ) -> AttributeSyntax ? {
1793+ attributes ? . first { attribute in
1794+ attribute . as ( AttributeSyntax . self ) ? . attributeName . trimmedDescription == " JSClass "
1795+ } ? . as ( AttributeSyntax . self )
1796+ }
1797+
17791798 static func hasAttribute ( _ attributes : AttributeListSyntax ? , name : String ) -> Bool {
17801799 guard let attributes else { return false }
17811800 return attributes . contains { attribute in
@@ -1784,7 +1803,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17841803 }
17851804 }
17861805
1787- /// Extracts the jsName argument value from a @JSSetter attribute, if present.
1806+ /// Extracts the ` jsName` argument value from an attribute, if present.
17881807 static func extractJSName ( from attribute : AttributeSyntax ) -> String ? {
17891808 guard let arguments = attribute . arguments ? . as ( LabeledExprListSyntax . self ) else {
17901809 return nil
@@ -1883,22 +1902,15 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
18831902
18841903 // MARK: - Property Name Resolution
18851904
1886- /// Helper for resolving property names from setter function names and jsName attributes
1905+ /// Helper for resolving property names from setter function names.
18871906 private struct PropertyNameResolver {
1888- /// Resolves property name and function base name from a setter function and optional jsName
1889- /// - Returns: (propertyName, functionBaseName) where propertyName preserves case for getter matching ,
1890- /// and functionBaseName has lowercase first char for ABI generation
1907+ /// Resolves property name and function base name from a setter function.
1908+ /// - Returns: (propertyName, functionBaseName) where ` propertyName` is derived from the setter name ,
1909+ /// and ` functionBaseName` has lowercase first char for ABI generation.
18911910 static func resolve (
18921911 functionName : String ,
1893- jsName : String ? ,
18941912 normalizeIdentifier : ( String ) -> String
18951913 ) -> ( propertyName : String , functionBaseName : String ) ? {
1896- if let jsName = jsName {
1897- let propertyName = normalizeIdentifier ( jsName )
1898- let functionBaseName = propertyName . prefix ( 1 ) . lowercased ( ) + propertyName . dropFirst ( )
1899- return ( propertyName : propertyName , functionBaseName : functionBaseName )
1900- }
1901-
19021914 let rawFunctionName =
19031915 functionName . hasPrefix ( " ` " ) && functionName . hasSuffix ( " ` " ) && functionName . count > 2
19041916 ? String ( functionName . dropFirst ( ) . dropLast ( ) )
@@ -1930,14 +1942,27 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19301942
19311943 private func enterJSClass ( _ typeName : String ) {
19321944 stateStack . append ( . jsClassBody ( name : typeName ) )
1933- currentType = CurrentType ( name : typeName , constructor : nil , methods : [ ] , getters : [ ] , setters : [ ] )
1945+ currentType = CurrentType ( name : typeName , jsName : nil , constructor : nil , methods : [ ] , getters : [ ] , setters : [ ] )
1946+ }
1947+
1948+ private func enterJSClass ( _ typeName : String , jsName : String ? ) {
1949+ stateStack . append ( . jsClassBody ( name : typeName ) )
1950+ currentType = CurrentType (
1951+ name : typeName ,
1952+ jsName : jsName ,
1953+ constructor : nil ,
1954+ methods : [ ] ,
1955+ getters : [ ] ,
1956+ setters : [ ]
1957+ )
19341958 }
19351959
19361960 private func exitJSClass ( ) {
19371961 if case . jsClassBody ( let typeName ) = state , let type = currentType , type . name == typeName {
19381962 importedTypes . append (
19391963 ImportedTypeSkeleton (
19401964 name : type . name ,
1965+ jsName : type . jsName ,
19411966 constructor : type . constructor ,
19421967 methods : type . methods ,
19431968 getters : type . getters ,
@@ -1952,7 +1977,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19521977
19531978 override func visit ( _ node : StructDeclSyntax ) -> SyntaxVisitorContinueKind {
19541979 if AttributeChecker . hasJSClassAttribute ( node . attributes ) {
1955- enterJSClass ( node . name . text )
1980+ let jsName = AttributeChecker . firstJSClassAttribute ( node . attributes ) . flatMap ( AttributeChecker . extractJSName )
1981+ enterJSClass ( node . name . text , jsName : jsName )
19561982 }
19571983 return . visitChildren
19581984 }
@@ -1965,7 +1991,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19651991
19661992 override func visit ( _ node : ClassDeclSyntax ) -> SyntaxVisitorContinueKind {
19671993 if AttributeChecker . hasJSClassAttribute ( node . attributes ) {
1968- enterJSClass ( node . name . text )
1994+ let jsName = AttributeChecker . firstJSClassAttribute ( node . attributes ) . flatMap ( AttributeChecker . extractJSName )
1995+ enterJSClass ( node . name . text , jsName : jsName )
19691996 }
19701997 return . visitChildren
19711998 }
@@ -2003,8 +2030,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20032030 }
20042031
20052032 private func handleTopLevelFunction ( _ node : FunctionDeclSyntax ) -> SyntaxVisitorContinueKind {
2006- if AttributeChecker . hasJSFunctionAttribute ( node . attributes ) ,
2007- let function = parseFunction ( node , enclosingTypeName : nil , isStaticMember : true )
2033+ if let jsFunction = AttributeChecker . firstJSFunctionAttribute ( node . attributes ) ,
2034+ let function = parseFunction ( jsFunction , node , enclosingTypeName : nil , isStaticMember : true )
20082035 {
20092036 importedFunctions . append ( function )
20102037 return . skipChildren
@@ -2028,13 +2055,13 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20282055 isStaticMember : Bool ,
20292056 type : inout CurrentType
20302057 ) -> Bool {
2031- if AttributeChecker . hasJSFunctionAttribute ( node . attributes ) {
2058+ if let jsFunction = AttributeChecker . firstJSFunctionAttribute ( node . attributes ) {
20322059 if isStaticMember {
2033- parseFunction ( node , enclosingTypeName : typeName , isStaticMember : true ) . map {
2060+ parseFunction ( jsFunction , node , enclosingTypeName : typeName , isStaticMember : true ) . map {
20342061 importedFunctions . append ( $0 )
20352062 }
20362063 } else {
2037- parseFunction ( node , enclosingTypeName : typeName , isStaticMember : false ) . map {
2064+ parseFunction ( jsFunction , node , enclosingTypeName : typeName , isStaticMember : false ) . map {
20382065 type . methods . append ( $0 )
20392066 }
20402067 }
@@ -2065,10 +2092,13 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20652092 guard AttributeChecker . hasJSGetterAttribute ( node . attributes ) else {
20662093 return . visitChildren
20672094 }
2095+ guard let jsGetter = AttributeChecker . firstJSGetterAttribute ( node . attributes ) else {
2096+ return . skipChildren
2097+ }
20682098
20692099 switch state {
20702100 case . topLevel :
2071- if let getter = parseGetterSkeleton ( node ) {
2101+ if let getter = parseGetterSkeleton ( jsGetter , node ) {
20722102 importedGlobalGetters . append ( getter )
20732103 }
20742104 return . skipChildren
@@ -2085,7 +2115,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20852115 " @JSGetter is not supported for static members. Use it only for instance members in @JSClass types. "
20862116 )
20872117 )
2088- } else if let getter = parseGetterSkeleton ( node ) {
2118+ } else if let getter = parseGetterSkeleton ( jsGetter , node ) {
20892119 type . getters . append ( getter )
20902120 currentType = type
20912121 }
@@ -2128,8 +2158,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21282158 private func collectStaticMembers ( in members : MemberBlockItemListSyntax , typeName : String ) {
21292159 for member in members {
21302160 if let function = member . decl . as ( FunctionDeclSyntax . self ) {
2131- if AttributeChecker . hasJSFunctionAttribute ( function . attributes ) ,
2132- let parsed = parseFunction ( function , enclosingTypeName : typeName , isStaticMember : true )
2161+ if let jsFunction = AttributeChecker . firstJSFunctionAttribute ( function . attributes ) ,
2162+ let parsed = parseFunction ( jsFunction , function , enclosingTypeName : typeName , isStaticMember : true )
21332163 {
21342164 importedFunctions . append ( parsed )
21352165 } else if AttributeChecker . hasJSSetterAttribute ( function . attributes ) {
@@ -2173,6 +2203,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21732203 }
21742204
21752205 private func parseFunction (
2206+ _ jsFunction : AttributeSyntax ,
21762207 _ node : FunctionDeclSyntax ,
21772208 enclosingTypeName : String ? ,
21782209 isStaticMember : Bool
@@ -2183,6 +2214,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21832214 }
21842215
21852216 let baseName = SwiftToSkeleton . normalizeIdentifier ( node . name . text )
2217+ let jsName = AttributeChecker . extractJSName ( from : jsFunction )
21862218 let name : String
21872219 if isStaticMember , let enclosingTypeName {
21882220 name = " \( enclosingTypeName ) _ \( baseName ) "
@@ -2202,6 +2234,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22022234 }
22032235 return ImportedFunctionSkeleton (
22042236 name : name ,
2237+ jsName : jsName ,
22052238 parameters : parameters ,
22062239 returnType : returnType ,
22072240 documentation : nil
@@ -2223,16 +2256,21 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22232256 return ( identifier , typeAnnotation . type )
22242257 }
22252258
2226- private func parseGetterSkeleton ( _ node : VariableDeclSyntax ) -> ImportedGetterSkeleton ? {
2259+ private func parseGetterSkeleton (
2260+ _ jsGetter : AttributeSyntax ,
2261+ _ node : VariableDeclSyntax
2262+ ) -> ImportedGetterSkeleton ? {
22272263 guard let ( identifier , type ) = extractPropertyInfo ( node ) else {
22282264 return nil
22292265 }
22302266 guard let propertyType = withLookupErrors ( { parent . lookupType ( for : type , errors : & $0 ) } ) else {
22312267 return nil
22322268 }
22332269 let propertyName = SwiftToSkeleton . normalizeIdentifier ( identifier . identifier . text )
2270+ let jsName = AttributeChecker . extractJSName ( from : jsGetter )
22342271 return ImportedGetterSkeleton (
22352272 name : propertyName ,
2273+ jsName : jsName ,
22362274 type : propertyType ,
22372275 documentation : nil ,
22382276 functionName : nil
@@ -2252,7 +2290,6 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22522290 guard
22532291 let ( propertyName , functionBaseName ) = PropertyNameResolver . resolve (
22542292 functionName : functionName ,
2255- jsName : validation . jsName ,
22562293 normalizeIdentifier : SwiftToSkeleton . normalizeIdentifier
22572294 )
22582295 else {
@@ -2261,6 +2298,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22612298
22622299 return ImportedSetterSkeleton (
22632300 name : propertyName ,
2301+ jsName : validation . jsName ,
22642302 type : validation . valueType ,
22652303 documentation : nil ,
22662304 functionName : " \( functionBaseName ) _set "
0 commit comments