@@ -62,6 +62,8 @@ struct BridgeJSLink {
62
62
var classLines : [ String ] = [ ]
63
63
var dtsExportLines : [ String ] = [ ]
64
64
var dtsClassLines : [ String ] = [ ]
65
+ var namespacedFunctions : [ ExportedFunction ] = [ ]
66
+ var namespacedClasses : [ ExportedClass ] = [ ]
65
67
66
68
if exportedSkeletons. contains ( where: { $0. classes. count > 0 } ) {
67
69
classLines. append (
@@ -83,10 +85,19 @@ struct BridgeJSLink {
83
85
exportsLines. append ( " \( klass. name) , " )
84
86
dtsExportLines. append ( contentsOf: dtsExportEntry)
85
87
dtsClassLines. append ( contentsOf: dtsType)
88
+
89
+ if klass. namespace != nil {
90
+ namespacedClasses. append ( klass)
91
+ }
86
92
}
87
93
88
94
for function in skeleton. functions {
89
95
var ( js, dts) = renderExportedFunction ( function: function)
96
+
97
+ if function. namespace != nil {
98
+ namespacedFunctions. append ( function)
99
+ }
100
+
90
101
js [ 0 ] = " \( function. name) : " + js[ 0 ]
91
102
js [ js. count - 1 ] += " , "
92
103
exportsLines. append ( contentsOf: js)
@@ -108,6 +119,36 @@ struct BridgeJSLink {
108
119
importObjectBuilders. append ( importObjectBuilder)
109
120
}
110
121
122
+ let hasNamespacedItems = !namespacedFunctions. isEmpty || !namespacedClasses. isEmpty
123
+
124
+ let exportsSection : String
125
+ if hasNamespacedItems {
126
+ let namespaceSetupCode = renderGlobalNamespace (
127
+ namespacedFunctions: namespacedFunctions,
128
+ namespacedClasses: namespacedClasses
129
+ )
130
+ . map { $0. indent ( count: 12 ) } . joined ( separator: " \n " )
131
+ exportsSection = """
132
+ \( classLines. map { $0. indent ( count: 12 ) } . joined ( separator: " \n " ) )
133
+ const exports = {
134
+ \( exportsLines. map { $0. indent ( count: 16 ) } . joined ( separator: " \n " ) )
135
+ };
136
+
137
+ \( namespaceSetupCode)
138
+
139
+ return exports;
140
+ },
141
+ """
142
+ } else {
143
+ exportsSection = """
144
+ \( classLines. map { $0. indent ( count: 12 ) } . joined ( separator: " \n " ) )
145
+ return {
146
+ \( exportsLines. map { $0. indent ( count: 16 ) } . joined ( separator: " \n " ) )
147
+ };
148
+ },
149
+ """
150
+ }
151
+
111
152
let outputJs = """
112
153
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
113
154
// DO NOT EDIT.
@@ -169,15 +210,13 @@ struct BridgeJSLink {
169
210
/** @param {WebAssembly.Instance} instance */
170
211
createExports: (instance) => {
171
212
const js = swift.memory.heap;
172
- \( classLines. map { $0. indent ( count: 12 ) } . joined ( separator: " \n " ) )
173
- return {
174
- \( exportsLines. map { $0. indent ( count: 16 ) } . joined ( separator: " \n " ) )
175
- };
176
- },
213
+ \( exportsSection)
177
214
}
178
215
}
179
216
"""
217
+
180
218
var dtsLines : [ String ] = [ ]
219
+ dtsLines. append ( contentsOf: namespaceDeclarations ( ) )
181
220
dtsLines. append ( contentsOf: dtsClassLines)
182
221
dtsLines. append ( " export type Exports = { " )
183
222
dtsLines. append ( contentsOf: dtsExportLines. map { $0. indent ( count: 4 ) } )
@@ -204,6 +243,102 @@ struct BridgeJSLink {
204
243
return ( outputJs, outputDts)
205
244
}
206
245
246
+ private func namespaceDeclarations( ) -> [ String ] {
247
+ var dtsLines : [ String ] = [ ]
248
+ var namespaceFunctions : [ String : [ ExportedFunction ] ] = [ : ]
249
+ var namespaceClasses : [ String : [ ExportedClass ] ] = [ : ]
250
+
251
+ for skeleton in exportedSkeletons {
252
+ for function in skeleton. functions {
253
+ if let namespace = function. namespace {
254
+ let namespaceKey = namespace. joined ( separator: " . " )
255
+ if namespaceFunctions [ namespaceKey] == nil {
256
+ namespaceFunctions [ namespaceKey] = [ ]
257
+ }
258
+ namespaceFunctions [ namespaceKey] ? . append ( function)
259
+ }
260
+ }
261
+
262
+ for klass in skeleton. classes {
263
+ if let classNamespace = klass. namespace {
264
+ let namespaceKey = classNamespace. joined ( separator: " . " )
265
+ if namespaceClasses [ namespaceKey] == nil {
266
+ namespaceClasses [ namespaceKey] = [ ]
267
+ }
268
+ namespaceClasses [ namespaceKey] ? . append ( klass)
269
+ }
270
+ }
271
+ }
272
+
273
+ guard !namespaceFunctions. isEmpty || !namespaceClasses. isEmpty else { return dtsLines }
274
+
275
+ dtsLines. append ( " export {}; " )
276
+ dtsLines. append ( " " )
277
+ dtsLines. append ( " declare global { " )
278
+
279
+ let identBaseSize = 4
280
+
281
+ for (namespacePath, classes) in namespaceClasses. sorted ( by: { $0. key < $1. key } ) {
282
+ let parts = namespacePath. split ( separator: " . " ) . map ( String . init)
283
+
284
+ for i in 0 ..< parts. count {
285
+ dtsLines. append ( " namespace \( parts [ i] ) { " . indent ( count: identBaseSize * ( i + 1 ) ) )
286
+ }
287
+
288
+ for klass in classes {
289
+ dtsLines. append ( " class \( klass. name) { " . indent ( count: identBaseSize * ( parts. count + 1 ) ) )
290
+
291
+ if let constructor = klass. constructor {
292
+ let constructorSignature =
293
+ " constructor( \( constructor. parameters. map { " \( $0. name) : \( $0. type. tsType) " } . joined ( separator: " , " ) ) ); "
294
+ dtsLines. append ( " \( constructorSignature) " . indent ( count: identBaseSize * ( parts. count + 2 ) ) )
295
+ }
296
+
297
+ for method in klass. methods {
298
+ let methodSignature =
299
+ " \( method. name) \( renderTSSignature ( parameters: method. parameters, returnType: method. returnType) ) ; "
300
+ dtsLines. append ( " \( methodSignature) " . indent ( count: identBaseSize * ( parts. count + 2 ) ) )
301
+ }
302
+
303
+ dtsLines. append ( " } " . indent ( count: identBaseSize * ( parts. count + 1 ) ) )
304
+ }
305
+
306
+ for i in ( 0 ..< parts. count) . reversed ( ) {
307
+ dtsLines. append ( " } " . indent ( count: identBaseSize * ( i + 1 ) ) )
308
+ }
309
+ }
310
+
311
+ for (namespacePath, functions) in namespaceFunctions. sorted ( by: { $0. key < $1. key } ) {
312
+ let parts = namespacePath. split ( separator: " . " ) . map ( String . init)
313
+
314
+ var namespaceExists = false
315
+ if namespaceClasses [ namespacePath] != nil {
316
+ namespaceExists = true
317
+ } else {
318
+ for i in 0 ..< parts. count {
319
+ dtsLines. append ( " namespace \( parts [ i] ) { " . indent ( count: identBaseSize * ( i + 1 ) ) )
320
+ }
321
+ }
322
+
323
+ for function in functions {
324
+ let signature =
325
+ " function \( function. name) \( renderTSSignature ( parameters: function. parameters, returnType: function. returnType) ) ; "
326
+ dtsLines. append ( " \( signature) " . indent ( count: identBaseSize * ( parts. count + 1 ) ) )
327
+ }
328
+
329
+ if !namespaceExists {
330
+ for i in ( 0 ..< parts. count) . reversed ( ) {
331
+ dtsLines. append ( " } " . indent ( count: identBaseSize * ( i + 1 ) ) )
332
+ }
333
+ }
334
+ }
335
+
336
+ dtsLines. append ( " } " )
337
+ dtsLines. append ( " " )
338
+
339
+ return dtsLines
340
+ }
341
+
207
342
class ExportedThunkBuilder {
208
343
var bodyLines : [ String ] = [ ]
209
344
var cleanupLines : [ String ] = [ ]
@@ -396,6 +531,53 @@ struct BridgeJSLink {
396
531
return ( jsLines, dtsTypeLines, dtsExportEntryLines)
397
532
}
398
533
534
+ func renderGlobalNamespace( namespacedFunctions: [ ExportedFunction ] , namespacedClasses: [ ExportedClass ] ) -> [ String ]
535
+ {
536
+ var lines : [ String ] = [ ]
537
+ var uniqueNamespaces : [ String ] = [ ]
538
+ var seen = Set < String > ( )
539
+
540
+ let functionNamespacePaths : Set < [ String ] > = Set (
541
+ namespacedFunctions
542
+ . compactMap { $0. namespace }
543
+ )
544
+ let classNamespacePaths : Set < [ String ] > = Set (
545
+ namespacedClasses
546
+ . compactMap { $0. namespace }
547
+ )
548
+
549
+ let allNamespacePaths =
550
+ functionNamespacePaths
551
+ . union ( classNamespacePaths)
552
+
553
+ allNamespacePaths. forEach { namespacePath in
554
+ namespacePath. makeIterator ( ) . enumerated ( ) . forEach { ( index, _) in
555
+ let path = namespacePath [ 0 ... index] . joined ( separator: " . " )
556
+ if seen. insert ( path) . inserted {
557
+ uniqueNamespaces. append ( path)
558
+ }
559
+ }
560
+ }
561
+
562
+ uniqueNamespaces. sorted ( ) . forEach { namespace in
563
+ lines. append ( " if (typeof globalThis. \( namespace) === 'undefined') { " )
564
+ lines. append ( " globalThis. \( namespace) = {}; " )
565
+ lines. append ( " } " )
566
+ }
567
+
568
+ namespacedClasses. forEach { klass in
569
+ let namespacePath : String = klass. namespace? . joined ( separator: " . " ) ?? " "
570
+ lines. append ( " globalThis. \( namespacePath) . \( klass. name) = exports. \( klass. name) ; " )
571
+ }
572
+
573
+ namespacedFunctions. forEach { function in
574
+ let namespacePath : String = function. namespace? . joined ( separator: " . " ) ?? " "
575
+ lines. append ( " globalThis. \( namespacePath) . \( function. name) = exports. \( function. name) ; " )
576
+ }
577
+
578
+ return lines
579
+ }
580
+
399
581
class ImportedThunkBuilder {
400
582
var bodyLines : [ String ] = [ ]
401
583
var parameterNames : [ String ] = [ ]
0 commit comments