@@ -504,6 +504,14 @@ public final class SwiftToSkeleton {
504504 enumDecl. attributes. hasJSAttribute ( )
505505 {
506506 swiftPath. insert ( enumDecl. name. text, at: 0 )
507+ } else if let structDecl = parent. as ( StructDeclSyntax . self) ,
508+ structDecl. attributes. hasJSAttribute ( )
509+ {
510+ swiftPath. insert ( structDecl. name. text, at: 0 )
511+ } else if let classDecl = parent. as ( ClassDeclSyntax . self) ,
512+ classDecl. attributes. hasJSAttribute ( )
513+ {
514+ swiftPath. insert ( classDecl. name. text, at: 0 )
507515 }
508516 currentNode = parent. parent
509517 }
@@ -648,6 +656,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
648656 var state : State {
649657 return stateStack. current
650658 }
659+
651660 let parent : SwiftToSkeleton
652661
653662 init ( parent: SwiftToSkeleton ) {
@@ -1453,6 +1462,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
14531462 guard namespaceResult. isValid else {
14541463 return . skipChildren
14551464 }
1465+ let effectiveNamespace = effectiveNamespace (
1466+ resolvedNamespace: namespaceResult. namespace,
1467+ parentTypeNamespace: computeParentTypeNamespace ( for: node)
1468+ )
14561469 let swiftCallName = SwiftToSkeleton . computeSwiftCallName ( for: node, itemName: name)
14571470 let explicitAccessControl = computeExplicitAtLeastInternalAccessControl (
14581471 for: node,
@@ -1466,10 +1479,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
14661479 constructor: nil ,
14671480 methods: [ ] ,
14681481 properties: [ ] ,
1469- namespace: namespaceResult . namespace ,
1482+ namespace: effectiveNamespace ,
14701483 identityMode: classIdentityMode
14711484 )
1472- let uniqueKey = makeKey ( name: name, namespace: namespaceResult . namespace )
1485+ let uniqueKey = makeKey ( name: name, namespace: effectiveNamespace )
14731486
14741487 stateStack. push ( state: . classBody( name: name, key: uniqueKey) )
14751488 exportedClassByName [ uniqueKey] = exportedClass
@@ -1558,6 +1571,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
15581571 guard namespaceResult. isValid else {
15591572 return . skipChildren
15601573 }
1574+ let effectiveNamespace = effectiveNamespace (
1575+ resolvedNamespace: namespaceResult. namespace,
1576+ parentTypeNamespace: computeParentTypeNamespace ( for: node)
1577+ )
15611578 let emitStyle = extractEnumStyle ( from: jsAttribute) ?? . const
15621579 let swiftCallName = SwiftToSkeleton . computeSwiftCallName ( for: node, itemName: name)
15631580 let explicitAccessControl = computeExplicitAtLeastInternalAccessControl (
@@ -1566,7 +1583,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
15661583 )
15671584
15681585 let tsFullPath : String
1569- if let namespace = namespaceResult . namespace , !namespace. isEmpty {
1586+ if let namespace = effectiveNamespace , !namespace. isEmpty {
15701587 tsFullPath = namespace. joined ( separator: " . " ) + " . " + name
15711588 } else {
15721589 tsFullPath = name
@@ -1580,13 +1597,13 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
15801597 explicitAccessControl: explicitAccessControl,
15811598 cases: [ ] , // Will be populated in visit(EnumCaseDeclSyntax)
15821599 rawType: SwiftEnumRawType ( rawType) ,
1583- namespace: namespaceResult . namespace ,
1600+ namespace: effectiveNamespace ,
15841601 emitStyle: emitStyle,
15851602 staticMethods: [ ] ,
15861603 staticProperties: [ ]
15871604 )
15881605
1589- let enumUniqueKey = makeKey ( name: name, namespace: namespaceResult . namespace )
1606+ let enumUniqueKey = makeKey ( name: name, namespace: effectiveNamespace )
15901607 exportedEnumByName [ enumUniqueKey] = exportedEnum
15911608 exportedEnumNames. append ( enumUniqueKey)
15921609
@@ -1685,18 +1702,22 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
16851702 guard namespaceResult. isValid else {
16861703 return . skipChildren
16871704 }
1705+ let effectiveNamespace = effectiveNamespace (
1706+ resolvedNamespace: namespaceResult. namespace,
1707+ parentTypeNamespace: computeParentTypeNamespace ( for: node)
1708+ )
16881709 _ = computeExplicitAtLeastInternalAccessControl (
16891710 for: node,
16901711 message: " Protocol visibility must be at least internal "
16911712 )
16921713
1693- let protocolUniqueKey = makeKey ( name: name, namespace: namespaceResult . namespace )
1714+ let protocolUniqueKey = makeKey ( name: name, namespace: effectiveNamespace )
16941715
16951716 exportedProtocolByName [ protocolUniqueKey] = ExportedProtocol (
16961717 name: name,
16971718 methods: [ ] ,
16981719 properties: [ ] ,
1699- namespace: namespaceResult . namespace
1720+ namespace: effectiveNamespace
17001721 )
17011722
17021723 stateStack. push ( state: . protocolBody( name: name, key: protocolUniqueKey) )
@@ -1707,7 +1728,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
17071728 if let exportedFunction = visitProtocolMethod (
17081729 node: funcDecl,
17091730 protocolName: name,
1710- namespace: namespaceResult . namespace
1731+ namespace: effectiveNamespace
17111732 ) {
17121733 methods. append ( exportedFunction)
17131734 }
@@ -1720,7 +1741,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
17201741 name: name,
17211742 methods: methods,
17221743 properties: exportedProtocolByName [ protocolUniqueKey] ? . properties ?? [ ] ,
1723- namespace: namespaceResult . namespace
1744+ namespace: effectiveNamespace
17241745 )
17251746
17261747 exportedProtocolByName [ protocolUniqueKey] = exportedProtocol
@@ -1742,6 +1763,10 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
17421763 guard namespaceResult. isValid else {
17431764 return . skipChildren
17441765 }
1766+ let effectiveNamespace = effectiveNamespace (
1767+ resolvedNamespace: namespaceResult. namespace,
1768+ parentTypeNamespace: computeParentTypeNamespace ( for: node)
1769+ )
17451770 let swiftCallName = SwiftToSkeleton . computeSwiftCallName ( for: node, itemName: name)
17461771 let explicitAccessControl = computeExplicitAtLeastInternalAccessControl (
17471772 for: node,
@@ -1791,22 +1816,22 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
17911816 type: fieldType,
17921817 isReadonly: true ,
17931818 isStatic: false ,
1794- namespace: namespaceResult . namespace ,
1819+ namespace: effectiveNamespace ,
17951820 staticContext: nil
17961821 )
17971822 properties. append ( property)
17981823 }
17991824 }
18001825 }
18011826
1802- let structUniqueKey = makeKey ( name: name, namespace: namespaceResult . namespace )
1827+ let structUniqueKey = makeKey ( name: name, namespace: effectiveNamespace )
18031828 let exportedStruct = ExportedStruct (
18041829 name: name,
18051830 swiftCallName: swiftCallName,
18061831 explicitAccessControl: explicitAccessControl,
18071832 properties: properties,
18081833 methods: [ ] ,
1809- namespace: namespaceResult . namespace
1834+ namespace: effectiveNamespace
18101835 )
18111836
18121837 exportedStructByName [ structUniqueKey] = exportedStruct
@@ -2035,6 +2060,34 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
20352060 return namespace. isEmpty ? nil : namespace
20362061 }
20372062
2063+ private func computeParentTypeNamespace( for node: some SyntaxProtocol ) -> [ String ] ? {
2064+ var path : [ String ] = [ ]
2065+ var currentNode : Syntax ? = node. parent
2066+
2067+ while let parent = currentNode {
2068+ if let structDecl = parent. as ( StructDeclSyntax . self) ,
2069+ structDecl. attributes. hasJSAttribute ( )
2070+ {
2071+ path. insert ( structDecl. name. text, at: 0 )
2072+ } else if let classDecl = parent. as ( ClassDeclSyntax . self) ,
2073+ classDecl. attributes. hasJSAttribute ( )
2074+ {
2075+ path. insert ( classDecl. name. text, at: 0 )
2076+ }
2077+ currentNode = parent. parent
2078+ }
2079+
2080+ return path. isEmpty ? nil : path
2081+ }
2082+
2083+ private func effectiveNamespace(
2084+ resolvedNamespace: [ String ] ? ,
2085+ parentTypeNamespace: [ String ] ?
2086+ ) -> [ String ] ? {
2087+ let combined = ( parentTypeNamespace ?? [ ] ) + ( resolvedNamespace ?? [ ] )
2088+ return combined. isEmpty ? nil : combined
2089+ }
2090+
20382091 /// Requires the node to have at least internal access control.
20392092 private func computeExplicitAtLeastInternalAccessControl(
20402093 for node: some WithModifiersSyntax ,
0 commit comments