Skip to content

Commit da01493

Browse files
committed
BridgeJS: Runtime tests, string enum fixes and code review feedback
1 parent 6333085 commit da01493

File tree

11 files changed

+1577
-166
lines changed

11 files changed

+1577
-166
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,12 @@ public class ExportSwift {
865865
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: callExpr).jsValue")))
866866
}
867867

868-
let retMutability = returnType == .string ? "var" : "let"
868+
let retMutability: String
869+
if returnType == .string {
870+
retMutability = "var"
871+
} else {
872+
retMutability = "let"
873+
}
869874
if returnType == .void {
870875
return CodeBlockItemSyntax(item: .init(ExpressionStmtSyntax(expression: callExpr)))
871876
} else {
@@ -952,7 +957,8 @@ public class ExportSwift {
952957
if rawType == .string {
953958
append(
954959
"""
955-
return ret.rawValue.withUTF8 { ptr in
960+
var rawValue = ret.rawValue
961+
return rawValue.withUTF8 { ptr in
956962
_swift_js_return_string(ptr.baseAddress, Int32(ptr.count))
957963
}
958964
"""

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ public struct ImportTS {
505505
}
506506
}
507507

508-
extension String {
508+
fileprivate extension String {
509509
func capitalizedFirstLetter() -> String {
510510
guard !isEmpty else { return self }
511511
return prefix(1).uppercased() + dropFirst()

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ struct BridgeJSLink {
7272
var namespacedEnums: [ExportedEnum] = []
7373
var enumConstantLines: [String] = []
7474
var dtsEnumLines: [String] = []
75+
var topLevelEnumLines: [String] = []
76+
var topLevelDtsEnumLines: [String] = []
7577

7678
if exportedSkeletons.contains(where: { $0.classes.count > 0 }) {
7779
classLines.append(
@@ -102,14 +104,29 @@ struct BridgeJSLink {
102104
if !skeleton.enums.isEmpty {
103105
for enumDefinition in skeleton.enums {
104106
let (jsEnum, dtsEnum) = try renderExportedEnum(enumDefinition)
105-
enumConstantLines.append(contentsOf: jsEnum)
106-
if enumDefinition.enumType != .namespace {
107+
108+
switch enumDefinition.enumType {
109+
case .namespace:
110+
break
111+
case .simple, .rawValue:
112+
var exportedJsEnum = jsEnum
113+
if !exportedJsEnum.isEmpty && exportedJsEnum[0].hasPrefix("const ") {
114+
exportedJsEnum[0] = "export " + exportedJsEnum[0]
115+
}
116+
topLevelEnumLines.append(contentsOf: exportedJsEnum)
117+
topLevelDtsEnumLines.append(contentsOf: dtsEnum)
118+
119+
if enumDefinition.namespace != nil {
120+
namespacedEnums.append(enumDefinition)
121+
}
122+
case .associatedValue:
123+
enumConstantLines.append(contentsOf: jsEnum)
107124
exportsLines.append("\(enumDefinition.name),")
108125
if enumDefinition.namespace != nil {
109126
namespacedEnums.append(enumDefinition)
110127
}
128+
dtsEnumLines.append(contentsOf: dtsEnum)
111129
}
112-
dtsEnumLines.append(contentsOf: dtsEnum)
113130
}
114131
}
115132

@@ -153,10 +170,11 @@ struct BridgeJSLink {
153170

154171
let exportsSection: String
155172
if hasNamespacedItems {
173+
let namespacedEnumsForExports = namespacedEnums.filter { $0.enumType == .associatedValue }
156174
let namespaceSetupCode = namespaceBuilder.renderGlobalNamespace(
157175
namespacedFunctions: namespacedFunctions,
158176
namespacedClasses: namespacedClasses,
159-
namespacedEnums: namespacedEnums
177+
namespacedEnums: namespacedEnumsForExports
160178
)
161179
.map { $0.indent(count: 12) }.joined(separator: "\n")
162180

@@ -189,14 +207,22 @@ struct BridgeJSLink {
189207
"""
190208
}
191209

210+
let topLevelEnumsSection = topLevelEnumLines.isEmpty ? "" : topLevelEnumLines.joined(separator: "\n") + "\n\n"
211+
212+
let topLevelNamespaceCode = namespaceBuilder.renderTopLevelEnumNamespaceAssignments(
213+
namespacedEnums: namespacedEnums
214+
)
215+
let namespaceAssignmentsSection =
216+
topLevelNamespaceCode.isEmpty ? "" : topLevelNamespaceCode.joined(separator: "\n") + "\n\n"
217+
192218
let outputJs = """
193219
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
194220
// DO NOT EDIT.
195221
//
196222
// To update this file, just rebuild your project or run
197223
// `swift package bridge-js`.
198224
199-
export async function createInstantiator(options, swift) {
225+
\(topLevelEnumsSection)\(namespaceAssignmentsSection)export async function createInstantiator(options, swift) {
200226
let instance;
201227
let memory;
202228
let setException;
@@ -270,14 +296,17 @@ struct BridgeJSLink {
270296
dtsLines.append("export type Imports = {")
271297
dtsLines.append(contentsOf: importObjectBuilders.flatMap { $0.dtsImportLines }.map { $0.indent(count: 4) })
272298
dtsLines.append("}")
299+
let topLevelDtsEnumsSection =
300+
topLevelDtsEnumLines.isEmpty ? "" : topLevelDtsEnumLines.joined(separator: "\n") + "\n"
301+
273302
let outputDts = """
274303
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
275304
// DO NOT EDIT.
276305
//
277306
// To update this file, just rebuild your project or run
278307
// `swift package bridge-js`.
279308
280-
\(dtsLines.joined(separator: "\n"))
309+
\(topLevelDtsEnumsSection)\(dtsLines.joined(separator: "\n"))
281310
export function createInstantiator(options: {
282311
imports: Imports;
283312
}, swift: any): Promise<{
@@ -535,7 +564,7 @@ struct BridgeJSLink {
535564
jsLines.append("const \(enumDefinition.name) = {")
536565
for (index, enumCase) in enumDefinition.cases.enumerated() {
537566
let caseName = enumCase.name.capitalizedFirstLetter
538-
jsLines.append(" \(caseName): \(index),".indent(count: 0))
567+
jsLines.append("\(caseName): \(index),".indent(count: 4))
539568
}
540569
jsLines.append("};")
541570
jsLines.append("")
@@ -546,15 +575,15 @@ struct BridgeJSLink {
546575
dtsLines.append("export enum \(enumDefinition.name) {")
547576
for (index, enumCase) in enumDefinition.cases.enumerated() {
548577
let caseName = enumCase.name.capitalizedFirstLetter
549-
dtsLines.append(" \(caseName) = \(index),")
578+
dtsLines.append("\(caseName) = \(index),".indent(count: 4))
550579
}
551580
dtsLines.append("}")
552581
dtsLines.append("")
553582
case .const:
554583
dtsLines.append("export const \(enumDefinition.name): {")
555584
for (index, enumCase) in enumDefinition.cases.enumerated() {
556585
let caseName = enumCase.name.capitalizedFirstLetter
557-
dtsLines.append(" readonly \(caseName): \(index);")
586+
dtsLines.append("readonly \(caseName): \(index);".indent(count: 4))
558587
}
559588
dtsLines.append("};")
560589
dtsLines.append(
@@ -608,7 +637,7 @@ struct BridgeJSLink {
608637
case "Float", "Double": formattedValue = rawValue
609638
default: formattedValue = rawValue
610639
}
611-
dtsLines.append(" \(caseName) = \(formattedValue),")
640+
dtsLines.append("\(caseName) = \(formattedValue),".indent(count: 4))
612641
}
613642
dtsLines.append("}")
614643
dtsLines.append("")
@@ -630,7 +659,7 @@ struct BridgeJSLink {
630659
formattedValue = rawValue
631660
}
632661

633-
dtsLines.append(" readonly \(caseName): \(formattedValue);")
662+
dtsLines.append("readonly \(caseName): \(formattedValue);".indent(count: 4))
634663
}
635664
dtsLines.append("};")
636665
dtsLines.append(
@@ -780,7 +809,7 @@ struct BridgeJSLink {
780809

781810
uniqueNamespaces.sorted().forEach { namespace in
782811
lines.append("if (typeof globalThis.\(namespace) === 'undefined') {")
783-
lines.append(" globalThis.\(namespace) = {};")
812+
lines.append("globalThis.\(namespace) = {};".indent(count: 4))
784813
lines.append("}")
785814
}
786815

@@ -790,8 +819,10 @@ struct BridgeJSLink {
790819
}
791820

792821
namespacedEnums.forEach { enumDefinition in
793-
let namespacePath: String = enumDefinition.namespace?.joined(separator: ".") ?? ""
794-
lines.append("globalThis.\(namespacePath).\(enumDefinition.name) = exports.\(enumDefinition.name);")
822+
if enumDefinition.enumType == .associatedValue {
823+
let namespacePath: String = enumDefinition.namespace?.joined(separator: ".") ?? ""
824+
lines.append("globalThis.\(namespacePath).\(enumDefinition.name) = exports.\(enumDefinition.name);")
825+
}
795826
}
796827

797828
namespacedFunctions.forEach { function in
@@ -1015,7 +1046,7 @@ struct BridgeJSLink {
10151046

10161047
uniqueNamespaces.sorted().forEach { namespace in
10171048
lines.append("if (typeof globalThis.\(namespace) === 'undefined') {")
1018-
lines.append(" globalThis.\(namespace) = {};")
1049+
lines.append("globalThis.\(namespace) = {};".indent(count: 4))
10191050
lines.append("}")
10201051
}
10211052

@@ -1037,6 +1068,44 @@ struct BridgeJSLink {
10371068
return lines
10381069
}
10391070

1071+
func renderTopLevelEnumNamespaceAssignments(namespacedEnums: [ExportedEnum]) -> [String] {
1072+
let topLevelNamespacedEnums = namespacedEnums.filter { $0.enumType == .simple || $0.enumType == .rawValue }
1073+
1074+
guard !topLevelNamespacedEnums.isEmpty else { return [] }
1075+
1076+
var lines: [String] = []
1077+
var uniqueNamespaces: [String] = []
1078+
var seen = Set<String>()
1079+
1080+
for enumDef in topLevelNamespacedEnums {
1081+
guard let namespacePath = enumDef.namespace else { continue }
1082+
namespacePath.enumerated().forEach { (index, _) in
1083+
let path = namespacePath[0...index].joined(separator: ".")
1084+
if !seen.contains(path) {
1085+
seen.insert(path)
1086+
uniqueNamespaces.append(path)
1087+
}
1088+
}
1089+
}
1090+
1091+
for namespace in uniqueNamespaces {
1092+
lines.append("if (typeof globalThis.\(namespace) === 'undefined') {")
1093+
lines.append("globalThis.\(namespace) = {};".indent(count: 4))
1094+
lines.append("}")
1095+
}
1096+
1097+
if !lines.isEmpty {
1098+
lines.append("")
1099+
}
1100+
1101+
for enumDef in topLevelNamespacedEnums {
1102+
let namespacePath = enumDef.namespace?.joined(separator: ".") ?? ""
1103+
lines.append("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);")
1104+
}
1105+
1106+
return lines
1107+
}
1108+
10401109
private struct NamespaceContent {
10411110
var functions: [ExportedFunction] = []
10421111
var classes: [ExportedClass] = []

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumNamespace.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
// Invalid to declare @JS(namespace) here as it would be conflicting with nesting
2222
@JS class HTTPServer {
2323
@JS init() {}
24-
@JS func call(_ method: Method)
24+
@JS func call(_ method: Method) {}
2525
}
2626
}
2727
}
@@ -49,7 +49,7 @@ enum Internal {
4949
}
5050
@JS class TestServer {
5151
@JS init() {}
52-
@JS func call(_ method: SupportedMethod)
52+
@JS func call(_ method: SupportedMethod) {}
5353
}
5454
}
5555

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
44
// To update this file, just rebuild your project or run
55
// `swift package bridge-js`.
66

7+
export const Direction = {
8+
North: 0,
9+
South: 1,
10+
East: 2,
11+
West: 3,
12+
};
13+
14+
export const Status = {
15+
Loading: 0,
16+
Success: 1,
17+
Error: 2,
18+
};
19+
20+
export const TSDirection = {
21+
North: 0,
22+
South: 1,
23+
East: 2,
24+
West: 3,
25+
};
26+
27+
728
export async function createInstantiator(options, swift) {
829
let instance;
930
let memory;
@@ -63,30 +84,7 @@ export async function createInstantiator(options, swift) {
6384
createExports: (instance) => {
6485
const js = swift.memory.heap;
6586

66-
const Direction = {
67-
North: 0,
68-
South: 1,
69-
East: 2,
70-
West: 3,
71-
};
72-
73-
const Status = {
74-
Loading: 0,
75-
Success: 1,
76-
Error: 2,
77-
};
78-
79-
const TSDirection = {
80-
North: 0,
81-
South: 1,
82-
East: 2,
83-
West: 3,
84-
};
85-
8687
return {
87-
Direction,
88-
Status,
89-
TSDirection,
9088
setDirection: function bjs_setDirection(direction) {
9189
instance.exports.bjs_setDirection(direction | 0);
9290
},

0 commit comments

Comments
 (0)