1 /**
2  A protocol that must be implemented to be a part of module's definition and the module definition itself.
3  */
4 public protocol AnyDefinition {}
5 
6 /**
7  The definition of the module. It is used to define some parameters
8  of the module and what it exports to the JavaScript world.
9  See `ModuleDefinitionBuilder` for more details on how to create it.
10  */
11 public final class ModuleDefinition: ObjectDefinition {
12   /**
13    The module's type associated with the definition. It's used to create the module instance.
14    */
15   var type: AnyModule.Type?
16 
17   /**
18    Name of the defined module. Falls back to the type name if not provided in the definition.
19    */
20   var name: String
21 
22   let eventListeners: [EventListener]
23   let viewManager: ViewManagerDefinition?
24 
25   /**
26    Names of the events that the module can send to JavaScript.
27    */
28   let eventNames: [String]
29 
30   /**
31    Initializer that is called by the `ModuleDefinitionBuilder` results builder.
32    */
33   override init(definitions: [AnyDefinition]) {
34     self.name = definitions
35       .compactMap { $0 as? ModuleNameDefinition }
36       .last?
37       .name ?? ""
38 
39     self.eventListeners = definitions.compactMap { $0 as? EventListener }
40 
41     self.viewManager = definitions
42       .compactMap { $0 as? ViewManagerDefinition }
43       .last
44 
45     self.eventNames = Array(
46       definitions
47         .compactMap { ($0 as? EventsDefinition)?.names }
48         .joined()
49     )
50 
51     super.init(definitions: definitions)
52   }
53 
54   /**
55    Sets the module type that the definition is associated with. We can't pass this in the initializer
56    as it's called by the results builder that doesn't have access to the type.
57    */
withTypenull58   func withType(_ type: AnyModule.Type) -> Self {
59     self.type = type
60 
61     // Use the type name if the name is not in the definition or was defined empty.
62     if name.isEmpty {
63       name = String(describing: type)
64     }
65     return self
66   }
67 
buildnull68   public override func build(appContext: AppContext) throws -> JavaScriptObject {
69     let object = try super.build(appContext: appContext)
70 
71     if let viewManager {
72       let reactComponentPrototype = try appContext.runtime.createObject()
73       try viewManager.decorateWithFunctions(object: reactComponentPrototype, appContext: appContext)
74       object.setProperty("ViewPrototype", value: reactComponentPrototype)
75     }
76 
77     // Give the module object a name. It's used for compatibility reasons, see `EventEmitter.ts`.
78     object.defineProperty("__expo_module_name__", value: name, options: [])
79 
80     return object
81   }
82 }
83 
84 /**
85  Module's name definition. Returned by `name()` in module's definition.
86  */
87 internal struct ModuleNameDefinition: AnyDefinition {
88   let name: String
89 }
90 
91 /**
92  A definition for module's constants. Returned by `constants(() -> SomeType)` in module's definition.
93  */
94 internal struct ConstantsDefinition: AnyDefinition {
95   let body: () -> [String: Any?]
96 }
97 
98 /**
99  A definition for module's events that can be sent to JavaScript.
100  */
101 public struct EventsDefinition: AnyDefinition {
102   let names: [String]
103 }
104