@@ -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