1 // Copyright 2021-present 650 Industries. All rights reserved. 2 3 /** 4 Base class for other definitions representing an object, such as `ModuleDefinition`. 5 */ 6 public class ObjectDefinition: AnyDefinition, JavaScriptObjectBuilder { 7 /** 8 A dictionary of functions defined by the object. 9 */ 10 let functions: [String: AnyFunction] 11 12 /** 13 An array of constants definitions. 14 */ 15 let constants: [ConstantsDefinition] 16 17 /** 18 A map of dynamic properties defined by the object. 19 */ 20 let properties: [String: AnyPropertyComponent] 21 22 /** 23 A map of classes defined within the object. 24 */ 25 let classes: [String: ClassComponent] 26 27 /** 28 Default initializer receiving children definitions from the result builder. 29 */ 30 init(definitions: [AnyDefinition]) { 31 self.functions = definitions 32 .compactMap { $0 as? AnyFunction } 33 .reduce(into: [String: AnyFunction]()) { dict, function in 34 dict[function.name] = function 35 } 36 37 self.constants = definitions 38 .compactMap { $0 as? ConstantsDefinition } 39 40 self.properties = definitions 41 .compactMap { $0 as? AnyPropertyComponent } 42 .reduce(into: [String: AnyPropertyComponent]()) { dict, property in 43 dict[property.name] = property 44 } 45 46 self.classes = definitions 47 .compactMap { $0 as? ClassComponent } 48 .reduce(into: [String: ClassComponent]()) { dict, klass in 49 dict[klass.name] = klass 50 } 51 } 52 53 /** 54 Merges all `constants` definitions into one dictionary. 55 */ 56 func getConstants() -> [String: Any?] { 57 return constants.reduce(into: [String: Any?]()) { dict, definition in 58 dict.merge(definition.body()) { $1 } 59 } 60 } 61 62 // MARK: - JavaScriptObjectBuilder 63 64 public func build(appContext: AppContext) throws -> JavaScriptObject { 65 let object = try appContext.runtime.createObject() 66 try decorate(object: object, appContext: appContext) 67 return object 68 } 69 70 public func decorate(object: JavaScriptObject, appContext: AppContext) throws { 71 let runtime = try appContext.runtime 72 73 decorateWithConstants(object: object) 74 try decorateWithFunctions(object: object, appContext: appContext) 75 try decorateWithProperties(object: object, appContext: appContext) 76 try decorateWithClasses(object: object, appContext: appContext) 77 } 78 79 // MARK: - Internals 80 81 internal func decorateWithConstants(object: JavaScriptObject) { 82 for (key, value) in getConstants() { 83 object.setProperty(key, value: value) 84 } 85 } 86 87 internal func decorateWithFunctions(object: JavaScriptObject, appContext: AppContext) throws { 88 for fn in functions.values { 89 object.setProperty(fn.name, value: try fn.build(appContext: appContext)) 90 } 91 } 92 93 internal func decorateWithProperties(object: JavaScriptObject, appContext: AppContext) throws { 94 for property in properties.values { 95 let descriptor = try property.buildDescriptor(appContext: appContext) 96 object.defineProperty(property.name, descriptor: descriptor) 97 } 98 } 99 100 internal func decorateWithClasses(object: JavaScriptObject, appContext: AppContext) throws { 101 for klass in classes.values { 102 object.setProperty(klass.name, value: try klass.build(appContext: appContext)) 103 } 104 } 105 } 106