expo-modules-macros 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/apple/ExpoModulesMacros
CHANGED
|
Binary file
|
|
@@ -8,9 +8,13 @@ import SwiftSyntaxMacros
|
|
|
8
8
|
/// level, so the author switches over it exhaustively with each payload keeping its static type, and
|
|
9
9
|
/// the macro synthesizes the conversion surface that makes it a JS boundary type:
|
|
10
10
|
///
|
|
11
|
+
/// - `decodableKinds`: the union of the payloads' `decodableKinds`, the JS kinds the union can decode.
|
|
11
12
|
/// - `decode(_:in:)`: an ordered decode that tries each case's payload converter in declaration order
|
|
12
13
|
/// and returns the first case that decodes; when none does, it throws
|
|
13
14
|
/// `Exceptions.UnionCaseMismatch` naming the union, the JS kind received, and the alternatives.
|
|
15
|
+
/// Only the overload taking a borrowed `JavaScriptUnownedValue` is emitted. The overload taking an
|
|
16
|
+
/// owning `JavaScriptValue` comes from the `JavaScriptDecodable` default, which borrows the value
|
|
17
|
+
/// and calls this one.
|
|
14
18
|
/// - `encode(_:in:)`: a `switch` over the cases, encoding the payload through its own type.
|
|
15
19
|
/// - `as(_:)`: one throwing overload per case, keyed by the payload's metatype, returning that payload
|
|
16
20
|
/// (`try source.as(String.self)` is `String`; `try? source.as(String.self)` is `String?`). It unwraps
|
|
@@ -57,6 +61,7 @@ public struct UnionMacro: MemberMacro, ExtensionMacro {
|
|
|
57
61
|
members.append(assertionMember)
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
members.append(decodableKindsProperty(union: union))
|
|
60
65
|
members.append(decodeMethod(union: union))
|
|
61
66
|
members.append(encodeMethod(union: union))
|
|
62
67
|
members.append(contentsOf: accessorMethods(union: union))
|
|
@@ -229,31 +234,50 @@ private func validatedUnion(of declaration: some DeclGroupSyntax) throws -> Unio
|
|
|
229
234
|
// MARK: - Synthesized members
|
|
230
235
|
|
|
231
236
|
/// `decode(_:in:)`: tries each case's payload converter in declaration order and returns the first
|
|
232
|
-
/// case that decodes.
|
|
237
|
+
/// case that decodes. Only the overload taking a borrowed `JavaScriptUnownedValue` is emitted; the
|
|
238
|
+
/// overload taking an owning `JavaScriptValue` comes from the `JavaScriptDecodable` default, which
|
|
239
|
+
/// borrows the value and calls this one. Each payload decode receives the same borrowed value. The
|
|
240
|
+
/// value's kind is read once, and each candidate is gated on its payload's `decodableKinds`, so a case
|
|
241
|
+
/// that can't accept that kind is skipped with a mask test instead of a thrown and discarded error. `try?` turns a candidate's failure into "try the next one" without erasing the
|
|
233
242
|
/// payload (each `payload` local keeps its concrete type); the candidate's own error is discarded, since
|
|
234
243
|
/// with several alternatives there is no single failure to surface. When no alternative accepts the
|
|
235
244
|
/// value the factory throws `Exceptions.UnionCaseMismatch`, naming the union, the JS kind of the value
|
|
236
|
-
/// received, and every payload type the union accepts.
|
|
245
|
+
/// received, and every payload type the union accepts. `JavaScriptUnownedValue` has no `kind`, so the
|
|
246
|
+
/// error reads it from an owning copy; the copy is made only on this failure path.
|
|
237
247
|
private func decodeMethod(union: UnionType) -> DeclSyntax {
|
|
238
|
-
var lines: [String] = []
|
|
248
|
+
var lines: [String] = [" let kind = JavaScriptValueKinds(of: value)"]
|
|
239
249
|
for unionCase in union.cases {
|
|
240
250
|
let payloadType = expressionType(unionCase.payloadType)
|
|
241
|
-
lines.append(" if let payload = try? \(payloadType).decode(value, in: runtime) {")
|
|
251
|
+
lines.append(" if \(payloadType).decodableKinds.contains(kind), let payload = try? \(payloadType).decode(value, in: runtime) {")
|
|
242
252
|
lines.append(" return \(unionCase.construction)")
|
|
243
253
|
lines.append(" }")
|
|
244
254
|
}
|
|
245
255
|
let expected = union.cases.map { "\"\($0.payloadType)\"" }.joined(separator: ", ")
|
|
246
|
-
let mismatch = "(unionName: \"\(union.name)\", received: value.kind.rawValue, expected: [\(expected)])"
|
|
256
|
+
let mismatch = "(unionName: \"\(union.name)\", received: value.copied(in: runtime).kind.rawValue, expected: [\(expected)])"
|
|
247
257
|
lines.append(" throw Exceptions.UnionCaseMismatch(\(mismatch))")
|
|
248
258
|
let body = lines.joined(separator: "\n")
|
|
249
259
|
return """
|
|
250
260
|
@JavaScriptActor
|
|
251
|
-
public static func decode(_ value: borrowing
|
|
261
|
+
public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws -> Self {
|
|
252
262
|
\(raw: body)
|
|
253
263
|
}
|
|
254
264
|
"""
|
|
255
265
|
}
|
|
256
266
|
|
|
267
|
+
/// `decodableKinds`: the union of the payloads' kinds, so a union nested in another union, or wrapped
|
|
268
|
+
/// in an optional, is skipped as precisely as its payloads are.
|
|
269
|
+
private func decodableKindsProperty(union: UnionType) -> DeclSyntax {
|
|
270
|
+
let kinds = union.cases.map { "\(expressionType($0.payloadType)).decodableKinds" }
|
|
271
|
+
// An option set's array literal is the union of its elements, which reads like the case list. The
|
|
272
|
+
// optimizer folds it to a constant after specialization, the same as a chain of `union(_:)` calls.
|
|
273
|
+
let expression = kinds.count == 1 ? kinds[0] : "[\(kinds.joined(separator: ", "))]"
|
|
274
|
+
return """
|
|
275
|
+
public static var decodableKinds: JavaScriptValueKinds {
|
|
276
|
+
return \(raw: expression)
|
|
277
|
+
}
|
|
278
|
+
"""
|
|
279
|
+
}
|
|
280
|
+
|
|
257
281
|
/// `encode(_:in:)`: a `switch` over the cases, each encoding its payload through the payload type's
|
|
258
282
|
/// own `encode`. Exhaustive by construction, so a case added later without re-expansion can't slip
|
|
259
283
|
/// through silently.
|