Skip to content

Commit 4fbb046

Browse files
authoredApr 23, 2025
Improve compatibility with Embedded Swift (#58)
The change adds a conditional import excluding the JavaScriptEventLoop module that's not yet fully compatible with Embedded Swift. With the `JSObject` type from the JavaScriptKit dependency, which is also now compatible with Embedded Swift, record types are now mapped to `JSObject`. As for fields marked as optional in originating IDL, these are now correctly mapped as optional in Swift.
1 parent beec492 commit 4fbb046

File tree

5 files changed

+35
-12
lines changed

5 files changed

+35
-12
lines changed
 

‎Sources/WebIDLToSwift/IDLBuilder.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import WebIDL
33

44
enum IDLBuilder {
55
static let basicDependencies = ["ECMAScript", "JavaScriptKit", "JavaScriptEventLoop"]
6+
static let optionalDependencies = ["JavaScriptEventLoop"]
67

78
static let preamble = """
89
// This file was auto-generated by WebIDLToSwift. DO NOT EDIT!
@@ -42,7 +43,11 @@ enum IDLBuilder {
4243
dependencies.append("JavaScriptBigIntSupport")
4344
}
4445

45-
let formedPreamble = preamble + dependencies.map { "import \($0)" }.joined(separator: "\n")
46+
let formedPreamble = preamble + (optionalDependencies.map { """
47+
#if canImport\($0)
48+
import \($0)
49+
#endif
50+
""" } + dependencies.map { "import \($0)" }).joined(separator: "\n")
4651

4752
try (formedPreamble + "\n\n" + content).write(toFile: path, atomically: true, encoding: .utf8)
4853
}
@@ -54,6 +59,7 @@ enum IDLBuilder {
5459
var contents: [SwiftSource] = []
5560

5661
var state = ScopedState.root(
62+
dictionaries: merged.dictionaries,
5763
interfaces: merged.interfaces,
5864
ignored: [
5965
// variadic callbacks are unsupported

‎Sources/WebIDLToSwift/MergeDeclarations.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ enum DeclarationMerger {
1818

1919
private static func enhanceMembers(_ members: [IDLNode]) -> [IDLNode] {
2020
members.flatMap { member -> [IDLNode] in
21+
if let named = member as? IDLNamed, named.name.contains("-") {
22+
return []
23+
}
24+
2125
if let operation = member as? IDLOperation,
2226
case .generic("Promise", _) = operation.idlType?.value
2327
{
@@ -223,6 +227,7 @@ enum DeclarationMerger {
223227
+ [Typedefs(typedefs: allTypes)]
224228
+ allNodes(ofType: IDLEnum.self)
225229
+ allNodes(ofType: IDLCallbackInterface.self),
230+
dictionaries: mergedDictionaries,
226231
interfaces: mergedInterfaces,
227232
types: mergedTypes
228233
// unions: unions
@@ -231,6 +236,7 @@ enum DeclarationMerger {
231236

232237
struct MergeResult {
233238
let declarations: [DeclarationFile]
239+
let dictionaries: [String: MergedDictionary]
234240
let interfaces: [String: MergedInterface]
235241
let types: [String: IDLTypealias]
236242
// let unions: Set<UnionType>

‎Sources/WebIDLToSwift/ModuleState.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct ScopedState {
6363
private(set) var this: SwiftSource!
6464
private(set) var className: SwiftSource!
6565
private(set) var interfaces: [String: MergedInterface]!
66+
private(set) var dictionaries: [String: MergedDictionary]!
6667
private(set) var ignored: [String: Set<String>]!
6768
private(set) var types: [String: IDLTypealias]!
6869
private(set) var override = false
@@ -106,12 +107,14 @@ struct ScopedState {
106107
}
107108

108109
static func root(
110+
dictionaries: [String: MergedDictionary],
109111
interfaces: [String: MergedInterface],
110112
ignored: [String: Set<String>],
111113
types: [String: IDLTypealias]
112114
) -> Self {
113115
var newState = ModuleState.current
114116
newState.interfaces = interfaces
117+
newState.dictionaries = dictionaries
115118
newState.ignored = ignored
116119
newState.types = types
117120
return newState

‎Sources/WebIDLToSwift/UnionType+SwiftRepresentable.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ extension UnionType: SwiftRepresentable {
7474
var initializers: [SwiftSource] {
7575
zip(sortedTypes, sortedNames).flatMap { (type, name) -> [SwiftSource] in
7676
let basicInitializer: [SwiftSource] = ["""
77-
init(_ \(name): \(type)) {
77+
public init(_ \(name): \(type)) {
7878
let val: \(self.name) = .\(name)(\(name))
7979
self = val
8080
}
@@ -162,10 +162,8 @@ extension SlimIDLType: SwiftRepresentable {
162162
case "FrozenArray", "ObservableArray":
163163
// ???
164164
return ["Element == \(args[0])"]
165-
case "Promise":
165+
case "Promise", "record":
166166
return []
167-
case "record":
168-
return ["Key == \(args[0])", "Value == \(args[1])"]
169167
default:
170168
fatalError("Unsupported generic type: \(name)")
171169
}
@@ -190,7 +188,7 @@ extension SlimIDLType.TypeValue: SwiftRepresentable {
190188
case "Promise":
191189
return "JSPromise"
192190
case "record":
193-
return "Dictionary"
191+
return "JSObject"
194192
default:
195193
fatalError("Unsupported generic type: \(name)")
196194
}
@@ -220,7 +218,7 @@ extension SlimIDLType.TypeValue: SwiftRepresentable {
220218
case "Promise":
221219
return "JSPromise"
222220
case "record":
223-
return "[\(args[0]): \(args[1])]"
221+
return "JSObject"
224222
default:
225223
fatalError("Unsupported generic type: \(name)")
226224
}

‎Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ extension IDLAttribute: SwiftRepresentable, Initializable {
8888
}
8989
}
9090

91+
extension IDLDictionary.Member {
92+
var isOptional: Bool {
93+
!required && !idlType.nullable && !idlType.isFunction
94+
}
95+
96+
var optionalSuffix: String {
97+
isOptional ? "?" : ""
98+
}
99+
}
100+
91101
extension MergedDictionary: SwiftRepresentable {
92102
var swiftRepresentation: SwiftSource {
93103
"""
@@ -98,7 +108,7 @@ extension MergedDictionary: SwiftRepresentable {
98108
"""
99109
}
100110

101-
private var membersWithPropertyWrapper: [(IDLDictionary.Member, SwiftSource)] {
111+
private func membersWithPropertyWrapper(_ members: [IDLDictionary.Member]) -> [(IDLDictionary.Member, SwiftSource)] {
102112
members.map {
103113
($0, $0.idlType.propertyWrapper(readonly: false))
104114
}
@@ -111,7 +121,7 @@ extension MergedDictionary: SwiftRepresentable {
111121
return """
112122
public convenience init(\(sequence: params)) {
113123
let object = JSObject.global[\(ModuleState.source(for: "Object"))].function!.new()
114-
\(lines: membersWithPropertyWrapper.map { member, wrapper in
124+
\(lines: membersWithPropertyWrapper(members).map { member, wrapper in
115125
if member.idlType.isFunction {
116126
return """
117127
\(wrapper)[\(ModuleState.source(for: member.name)), in: object] = \(member.name)
@@ -126,7 +136,7 @@ extension MergedDictionary: SwiftRepresentable {
126136
}
127137
128138
public required init(unsafelyWrapping object: JSObject) {
129-
\(lines: membersWithPropertyWrapper.map { member, wrapper in
139+
\(lines: membersWithPropertyWrapper(members).map { member, wrapper in
130140
"_\(raw: member.name) = \(wrapper)(jsObject: object, name: \(ModuleState.source(for: member.name)))"
131141
})
132142
super.init(unsafelyWrapping: object)
@@ -135,10 +145,10 @@ extension MergedDictionary: SwiftRepresentable {
135145
}
136146

137147
private var swiftMembers: [SwiftSource] {
138-
membersWithPropertyWrapper.map { member, wrapper in
148+
self.membersWithPropertyWrapper(members).map { member, wrapper in
139149
"""
140150
@\(wrapper)
141-
public var \(member.name): \(member.idlType)
151+
public var \(member.name): \(member.idlType)\(member.optionalSuffix)
142152
"""
143153
}
144154
}

0 commit comments

Comments
 (0)